use std::{fmt::{
Display,
Debug
}, sync::Arc};
use log::{error, info};
use reqwest::{StatusCode, IntoUrl, header::CONTENT_TYPE};
use serde::{Serialize, Deserialize};
use async_lock::RwLock;
use crate::api::{
result::{
BotXApiError,
ExpressResult,
BotXApiResult,
},
context::BotXApiContext, v2::token::api::token, models::FileResponse
};
#[derive(Debug)]
pub struct MultipartPartData {
pub name: String,
pub file_name: Option<String>,
pub content: MultipartPartDataContent,
pub mime_type: Option<String>,
}
#[derive(Debug)]
pub enum MultipartPartDataContent {
Bytes(Vec<u8>),
Text(String)
}
pub struct RequestManager {}
impl RequestManager {
pub const JSON_CONTENT_TYPE: &str = "application/json";
pub const MULTIPART_CONTENT_TYPE: &str = "multipart/form-data";
pub async fn post<TRequest, TResponse, TError>(request_name: impl Into<String> + Display, url: impl IntoUrl + Debug, request: &TRequest, content_type: impl Into<String> + Display, context: &Arc<RwLock<BotXApiContext>>) -> BotXApiResult<TResponse, TError>
where
TRequest: Serialize,
for<'a> TResponse: Deserialize<'a> + Debug,
for<'a> TError: Deserialize<'a> + Debug,
{
log::info!("{request_name} request url [{url:?}]");
log::debug!("{request_name} request data [{}]", serde_json::to_string(request).unwrap());
let context_read_lock = context.read().await;
let raw_response = if context_read_lock.auth_token.is_some() {
let auth_token = context_read_lock.auth_token.as_ref().map(|x| x.clone()).unwrap();
let raw_response = context_read_lock.client.post(url)
.bearer_auth(auth_token)
.header(reqwest::header::CONTENT_TYPE, content_type.into())
.json(request)
.send()
.await
.map_err::<BotXApiError<TError>, _>(|e| {
log::error!("{request_name} request error:[{e:#?}]");
e.into()
})?;
drop(context_read_lock);
raw_response
} else {
info!("Не найден токен авторизации в контексте бота. Пере запрашиваем токен авторизации");
drop(context_read_lock);
let mut context_write_lock = context.write().await;
let token_response = token(&mut *context_write_lock).await;
let Ok(token_result) = token_response else {
error!("Не удалось повторить запрос с пере авторизацией");
return Err(BotXApiError::Unauthorized);
};
context_write_lock.auth_token = Some(token_result.token.clone());
let context_read_lock = async_lock::RwLockWriteGuard::<'_, BotXApiContext>::downgrade(context_write_lock);
let auth_token = token_result.token;
let raw_response = context_read_lock.client.post(url)
.bearer_auth(auth_token)
.header(reqwest::header::CONTENT_TYPE, content_type.into())
.json(request)
.send()
.await
.map_err::<BotXApiError<TError>, _>(|e| {
log::error!("{request_name} request error:[{e:#?}]");
e.into()
})?;
drop(context_read_lock);
raw_response
};
let status_code = raw_response.status();
let response_body = raw_response.text()
.await
.map_err::<BotXApiError<TError>, _>(|e| {
log::error!("{request_name} read response error:[{e:#?}]");
e.into()
})?;
if status_code == StatusCode::UNAUTHORIZED {
log::debug!("{request_name} request Unauthorized {response_body}");
return Err(BotXApiError::Unauthorized);
}
let response = serde_json::from_str::<ExpressResult<TResponse, TError>>(&*response_body)
.map_err(|e| {
log::error!("{request_name} response body deserialization error: [{response_body}]");
BotXApiError::SerdeError(e)
})?;
if !response.is_ok() {
log::error!("{request_name} response status:[{status_code}] raw body:[{response_body}] deserialized body:[{response:#?}]");
} else {
log::debug!("{request_name} response status:[{status_code}] raw body:[{response_body}] deserialized body:[{response:#?}]");
}
response.into()
}
pub async fn put<TRequest, TResponse, TError>(request_name: impl Into<String> + Display, url: impl IntoUrl + Debug, request: &TRequest, content_type: impl Into<String> + Display, context: &Arc<RwLock<BotXApiContext>>) -> BotXApiResult<TResponse, TError>
where
TRequest: Serialize,
for<'a> TResponse: Deserialize<'a> + Debug,
for<'a> TError: Deserialize<'a> + Debug,
{
log::info!("{request_name} request url [{url:?}]");
log::debug!("{request_name} request data [{}]", serde_json::to_string(request).unwrap());
let context_read_lock = context.read().await;
let raw_response = if context_read_lock.auth_token.is_some() {
let auth_token = context_read_lock.auth_token.as_ref().map(|x| x.clone()).unwrap();
let raw_response = context_read_lock.client.put(url)
.bearer_auth(auth_token)
.header(reqwest::header::CONTENT_TYPE, content_type.into())
.json(request)
.send()
.await
.map_err::<BotXApiError<TError>, _>(|e| {
log::error!("{request_name} request error:[{e:#?}]");
e.into()
})?;
drop(context_read_lock);
raw_response
} else {
info!("Не найден токен авторизации в контексте бота. Пере запрашиваем токен авторизации");
drop(context_read_lock);
let mut context_write_lock = context.write().await;
let token_response = token(&mut *context_write_lock).await;
let Ok(token_result) = token_response else {
error!("Не удалось повторить запрос с пере авторизацией");
return Err(BotXApiError::Unauthorized);
};
context_write_lock.auth_token = Some(token_result.token.clone());
let context_read_lock = async_lock::RwLockWriteGuard::<'_, BotXApiContext>::downgrade(context_write_lock);
let auth_token = token_result.token;
let raw_response = context_read_lock.client.put(url)
.bearer_auth(auth_token)
.header(reqwest::header::CONTENT_TYPE, content_type.into())
.json(request)
.send()
.await
.map_err::<BotXApiError<TError>, _>(|e| {
log::error!("{request_name} request error:[{e:#?}]");
e.into()
})?;
drop(context_read_lock);
raw_response
};
let status_code = raw_response.status();
let response_body = raw_response.text()
.await
.map_err::<BotXApiError<TError>, _>(|e| {
log::error!("{request_name} read response error:[{e:#?}]");
e.into()
})?;
if status_code == StatusCode::UNAUTHORIZED {
log::debug!("{request_name} request Unauthorized {response_body}");
return Err(BotXApiError::Unauthorized);
}
let response = serde_json::from_str::<ExpressResult<TResponse, TError>>(&*response_body)
.map_err(|e| {
log::error!("{request_name} response body deserialization error: [{response_body}]");
BotXApiError::SerdeError(e)
})?;
if !response.is_ok() {
log::error!("{request_name} response status:[{status_code}] raw body:[{response_body}] deserialized body:[{response:#?}]");
} else {
log::debug!("{request_name} response status:[{status_code}] raw body:[{response_body}] deserialized body:[{response:#?}]");
}
response.into()
}
pub async fn get<TResponse, TError>(request_name: impl Into<String> + Display, url: impl IntoUrl + Debug, content_type: impl Into<String> + Display, context: &Arc<RwLock<BotXApiContext>>) -> BotXApiResult<TResponse, TError>
where
for<'a> TResponse: Deserialize<'a> + Debug,
for<'a> TError: Deserialize<'a> + Debug,
{
log::info!("{request_name} request url [{url:?}]");
let context_read_lock = context.read().await;
let raw_response = if context_read_lock.auth_token.is_some() {
let auth_token = context_read_lock.auth_token.as_ref().map(|x| x.clone()).unwrap();
let raw_response = context_read_lock.client.get(url)
.bearer_auth(auth_token)
.header(reqwest::header::CONTENT_TYPE, content_type.into())
.send()
.await
.map_err::<BotXApiError<TError>, _>(|e| {
log::error!("{request_name} request error:[{e:#?}]");
e.into()
})?;
drop(context_read_lock);
raw_response
} else {
info!("Не найден токен авторизации в контексте бота. Пере запрашиваем токен авторизации");
drop(context_read_lock);
let mut context_write_lock = context.write().await;
let token_response = token(&mut *context_write_lock).await;
let Ok(token_result) = token_response else {
error!("Не удалось повторить запрос с пере авторизацией");
return Err(BotXApiError::Unauthorized);
};
context_write_lock.auth_token = Some(token_result.token.clone());
let context_read_lock = async_lock::RwLockWriteGuard::<'_, BotXApiContext>::downgrade(context_write_lock);
let auth_token = token_result.token;
let raw_response = context_read_lock.client.get(url)
.bearer_auth(auth_token)
.header(reqwest::header::CONTENT_TYPE, content_type.into())
.send()
.await
.map_err::<BotXApiError<TError>, _>(|e| {
log::error!("{request_name} request error:[{e:#?}]");
e.into()
})?;
drop(context_read_lock);
raw_response
};
let status_code = raw_response.status();
let response_body = raw_response.text()
.await
.map_err::<BotXApiError<TError>, _>(|e| {
log::error!("{request_name} read response error:[{e:#?}]");
e.into()
})?;
if status_code == StatusCode::UNAUTHORIZED {
log::debug!("{request_name} request Unauthorized {response_body}");
return Err(BotXApiError::Unauthorized);
}
let response = serde_json::from_str::<ExpressResult<TResponse, TError>>(&*response_body)
.map_err(|e| {
log::error!("{request_name} response body deserialization error: [{response_body}]");
BotXApiError::SerdeError(e)
})?;
if !response.is_ok() {
log::error!("{request_name} response status:[{status_code}] raw body:[{response_body}] deserialized body:[{response:#?}]");
} else {
log::debug!("{request_name} response status:[{status_code}] raw body:[{response_body}] deserialized body:[{response:#?}]");
}
response.into()
}
pub async fn get_file<TError>(request_name: impl Into<String> + Display, url: impl IntoUrl + Debug, content_type: impl Into<String> + Display, context: &Arc<RwLock<BotXApiContext>>) -> BotXApiResult<FileResponse, TError>
where
for<'a> TError: Deserialize<'a> + Debug,
{
log::info!("{request_name} request url [{url:?}]");
let context_read_lock = context.read().await;
let raw_response = if context_read_lock.auth_token.is_some() {
let auth_token = context_read_lock.auth_token.as_ref().map(|x| x.clone()).unwrap();
let raw_response = context_read_lock.client.get(url)
.bearer_auth(auth_token)
.header(reqwest::header::CONTENT_TYPE, content_type.into())
.send()
.await
.map_err::<BotXApiError<TError>, _>(|e| {
log::error!("{request_name} request error:[{e:#?}]");
e.into()
})?;
drop(context_read_lock);
raw_response
} else {
info!("Не найден токен авторизации в контексте бота. Пере запрашиваем токен авторизации");
drop(context_read_lock);
let mut context_write_lock = context.write().await;
let token_response = token(&mut *context_write_lock).await;
let Ok(token_result) = token_response else {
error!("Не удалось повторить запрос с пере авторизацией");
return Err(BotXApiError::Unauthorized);
};
context_write_lock.auth_token = Some(token_result.token.clone());
let context_read_lock = async_lock::RwLockWriteGuard::<'_, BotXApiContext>::downgrade(context_write_lock);
let auth_token = token_result.token;
let raw_response = context_read_lock.client.get(url)
.bearer_auth(auth_token)
.header(reqwest::header::CONTENT_TYPE, content_type.into())
.send()
.await
.map_err::<BotXApiError<TError>, _>(|e| {
log::error!("{request_name} request error:[{e:#?}]");
e.into()
})?;
drop(context_read_lock);
raw_response
};
let status_code = raw_response.status();
let result = if [200u16, 201u16].contains(&raw_response.status().as_u16()) {
let content_type = raw_response.headers()
.get(CONTENT_TYPE)
.map(|x| x.to_str()
.unwrap()
.to_string())
.unwrap_or("text/plain".to_string());
let data = raw_response.bytes()
.await
.map_err::<BotXApiError<TError>, _>(|e| {
log::error!("{request_name} read response error:[{e:#?}]");
e.into()
})?
.to_vec();
ExpressResult::Ok(FileResponse { content_type: serde_json::from_str(&*format!("\"{content_type}\"")).unwrap(), data })
} else {
let response_body = raw_response.text()
.await
.map_err::<BotXApiError<TError>, _>(|e| {
log::error!("{request_name} read response error:[{e:#?}]");
e.into()
})?;
if status_code == StatusCode::UNAUTHORIZED {
log::debug!("{request_name} request Unauthorized {response_body}");
return Err(BotXApiError::Unauthorized);
}
let response_error = serde_json::from_str::<TError>(&*response_body).map_err(|e| {
log::error!("{request_name} response body deserialization error: [{response_body}]");
BotXApiError::SerdeError(e)
})?;
log::error!("{request_name} response status:[{status_code}] raw body:[{response_body}] deserialized body:[{response_error:#?}]");
ExpressResult::Err(response_error)
};
result.into()
}
pub async fn delete<TResponse, TError>(request_name: impl Into<String> + Display, url: impl IntoUrl + Debug, content_type: impl Into<String> + Display, context: &Arc<RwLock<BotXApiContext>>) -> BotXApiResult<TResponse, TError>
where
for<'a> TResponse: Deserialize<'a> + Debug,
for<'a> TError: Deserialize<'a> + Debug,
{
log::info!("{request_name} request url [{url:?}]");
let context_read_lock = context.read().await;
let raw_response = if context_read_lock.auth_token.is_some() {
let auth_token = context_read_lock.auth_token.as_ref().map(|x| x.clone()).unwrap();
let raw_response = context_read_lock.client.delete(url)
.bearer_auth(auth_token)
.header(reqwest::header::CONTENT_TYPE, content_type.into())
.send()
.await
.map_err::<BotXApiError<TError>, _>(|e| {
log::error!("{request_name} request error:[{e:#?}]");
e.into()
})?;
drop(context_read_lock);
raw_response
} else {
info!("Не найден токен авторизации в контексте бота. Пере запрашиваем токен авторизации");
drop(context_read_lock);
let mut context_write_lock = context.write().await;
let token_response = token(&mut *context_write_lock).await;
let Ok(token_result) = token_response else {
error!("Не удалось повторить запрос с пере авторизацией");
return Err(BotXApiError::Unauthorized);
};
context_write_lock.auth_token = Some(token_result.token.clone());
let context_read_lock = async_lock::RwLockWriteGuard::<'_, BotXApiContext>::downgrade(context_write_lock);
let auth_token = token_result.token;
let raw_response = context_read_lock.client.delete(url)
.bearer_auth(auth_token)
.header(reqwest::header::CONTENT_TYPE, content_type.into())
.send()
.await
.map_err::<BotXApiError<TError>, _>(|e| {
log::error!("{request_name} request error:[{e:#?}]");
e.into()
})?;
drop(context_read_lock);
raw_response
};
let status_code = raw_response.status();
let response_body = raw_response.text()
.await
.map_err::<BotXApiError<TError>, _>(|e| {
log::error!("{request_name} read response error:[{e:#?}]");
e.into()
})?;
if status_code == StatusCode::UNAUTHORIZED {
log::debug!("{request_name} request Unauthorized {response_body}");
return Err(BotXApiError::Unauthorized);
}
let response = serde_json::from_str::<ExpressResult<TResponse, TError>>(&*response_body)
.map_err(|e| {
log::error!("{request_name} response body deserialization error: [{response_body}]");
BotXApiError::SerdeError(e)
})?;
if !response.is_ok() {
log::error!("{request_name} response status:[{status_code}] raw body:[{response_body}] deserialized body:[{response:#?}]");
} else {
log::debug!("{request_name} response status:[{status_code}] raw body:[{response_body}] deserialized body:[{response:#?}]");
}
response.into()
}
pub async fn post_multipart<TResponse, TError>(request_name: impl Into<String> + Display, url: impl IntoUrl + Debug, data: Vec<MultipartPartData>, context: &Arc<RwLock<BotXApiContext>>) -> BotXApiResult<TResponse, TError>
where
for<'a> TResponse: Deserialize<'a> + Debug,
for<'a> TError: Deserialize<'a> + Debug,
{
log::info!("{request_name} request url [{url:?}]");
log::debug!("{request_name} request data [{:#?}]", data);
let mut form = reqwest::multipart::Form::new();
for data in data.into_iter() {
let part = match data.content {
MultipartPartDataContent::Bytes(bytes) => reqwest::multipart::Part::bytes(bytes),
MultipartPartDataContent::Text(text) => reqwest::multipart::Part::text(text),
};
let part = if let Some(file_name) = data.file_name {
part.file_name(file_name)
} else {
part
};
let part = if let Some(content_type) = data.mime_type {
part.mime_str(&*content_type).unwrap()
} else {
part
};
form = form.part(data.name, part);
}
log::debug!("{:#?}", form);
let context_read_lock = context.read().await;
let raw_response = if context_read_lock.auth_token.is_some() {
let auth_token = context_read_lock.auth_token.as_ref().map(|x| x.clone()).unwrap();
let raw_response = context_read_lock.client.post(url)
.bearer_auth(auth_token)
.multipart(form)
.send()
.await
.map_err::<BotXApiError<TError>, _>(|e| {
log::error!("{request_name} request error:[{e:#?}]");
e.into()
})?;
drop(context_read_lock);
raw_response
} else {
info!("Не найден токен авторизации в контексте бота. Пере запрашиваем токен авторизации");
drop(context_read_lock);
let mut context_write_lock = context.write().await;
let token_response = token(&mut *context_write_lock).await;
let Ok(token_result) = token_response else {
error!("Не удалось повторить запрос с пере авторизацией");
return Err(BotXApiError::Unauthorized);
};
context_write_lock.auth_token = Some(token_result.token.clone());
let context_read_lock = async_lock::RwLockWriteGuard::<'_, BotXApiContext>::downgrade(context_write_lock);
let auth_token = token_result.token;
let raw_response = context_read_lock.client.post(url)
.bearer_auth(auth_token)
.multipart(form)
.send()
.await
.map_err::<BotXApiError<TError>, _>(|e| {
log::error!("{request_name} request error:[{e:#?}]");
e.into()
})?;
drop(context_read_lock);
raw_response
};
let status_code = raw_response.status();
let response_body = raw_response.text()
.await
.map_err::<BotXApiError<TError>, _>(|e| {
log::error!("{request_name} read response error:[{e:#?}]");
e.into()
})?;
if status_code == StatusCode::UNAUTHORIZED {
log::debug!("{request_name} request Unauthorized {response_body}");
return Err(BotXApiError::Unauthorized);
}
let response = serde_json::from_str::<ExpressResult<TResponse, TError>>(&*response_body)
.map_err(|e| {
log::error!("{request_name} response body deserialization error: [{response_body}]");
BotXApiError::SerdeError(e)
})?;
if !response.is_ok() {
log::error!("{request_name} response status:[{status_code}] raw body:[{response_body}] deserialized body:[{response:#?}]");
} else {
log::debug!("{request_name} response status:[{status_code}] raw body:[{response_body}] deserialized body:[{response:#?}]");
}
response.into()
}
}