mod auth;
mod card;
mod media;
mod message;
mod response;
#[cfg(test)]
pub(crate) mod test_support;
mod transport;
mod ws;
use std::fmt;
use std::sync::Arc;
use serde::Serialize;
use serde::de::DeserializeOwned;
use crate::{ChannelConfig, Result};
pub use auth::{AppAccessTokenResponse, TenantAccessTokenResponse};
pub use card::CardUpdateOptions;
pub use media::{
FileCreateRequest, FileKey, FileType, ImageCreateRequest, ImageKey, ImageType,
MAX_FILE_UPLOAD_BYTES, MAX_IMAGE_UPLOAD_BYTES, MAX_MESSAGE_RESOURCE_BYTES, MessageResourceType,
};
pub use message::{MessageCreateOptions, MessageReplyOptions};
#[cfg(feature = "reqwest-transport")]
pub use transport::ReqwestOpenApiTransport;
pub use transport::{
BinaryHttpResponse, BoxFuture, HttpMethod, HttpRequest, HttpResponse, MultipartPart,
MultipartRequest, OpenApiBinaryTransport, OpenApiMultipartTransport, OpenApiTransport,
};
#[cfg(feature = "websocket")]
pub(crate) use ws::WebSocketConnectionItem;
#[cfg(feature = "websocket")]
pub use ws::{TokioTungsteniteWebSocketTransport, WebSocketConnection};
pub use ws::{
WebSocketClientConfig, WebSocketEndpoint, WebSocketEvent, WebSocketEventAck,
WebSocketEventFrame, WebSocketFrame, WebSocketFrameMethod, WebSocketHeader,
WebSocketMessageType,
};
use auth::AccessTokenCache;
use response::parse_openapi_response;
#[derive(Clone)]
pub struct OpenApiClient<T> {
config: ChannelConfig,
transport: T,
app_access_token_cache: Arc<AccessTokenCache>,
tenant_access_token_cache: Arc<AccessTokenCache>,
}
impl<T> fmt::Debug for OpenApiClient<T> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("OpenApiClient")
.field("config", &self.config)
.field("transport_type", &std::any::type_name::<T>())
.finish_non_exhaustive()
}
}
impl<T> OpenApiClient<T>
where
T: OpenApiTransport,
{
pub fn new(config: ChannelConfig, transport: T) -> Self {
Self {
config,
transport,
app_access_token_cache: Arc::new(AccessTokenCache::default()),
tenant_access_token_cache: Arc::new(AccessTokenCache::default()),
}
}
pub fn config(&self) -> &ChannelConfig {
&self.config
}
pub(crate) fn transport(&self) -> &T {
&self.transport
}
pub async fn post_openapi_json<B, R>(&self, path: &str, body: &B) -> Result<R>
where
B: Serialize + ?Sized,
R: DeserializeOwned,
{
let request = self.post_json_request(path, body)?;
let response = self.transport.send_json(request).await?;
parse_openapi_response(response)
}
pub async fn post_tenant_json<B, R>(&self, path: &str, body: &B) -> Result<R>
where
B: Serialize + ?Sized,
R: DeserializeOwned,
{
self.tenant_json(HttpMethod::Post, path, body).await
}
pub async fn put_tenant_json<B, R>(&self, path: &str, body: &B) -> Result<R>
where
B: Serialize + ?Sized,
R: DeserializeOwned,
{
self.tenant_json(HttpMethod::Put, path, body).await
}
pub async fn patch_tenant_json<B, R>(&self, path: &str, body: &B) -> Result<R>
where
B: Serialize + ?Sized,
R: DeserializeOwned,
{
self.tenant_json(HttpMethod::Patch, path, body).await
}
async fn tenant_json<B, R>(&self, method: HttpMethod, path: &str, body: &B) -> Result<R>
where
B: Serialize + ?Sized,
R: DeserializeOwned,
{
let request = self.json_request(method, path, body)?;
let token = self.tenant_access_token().await?;
let request = request.with_bearer_auth(token);
let response = self.transport.send_json(request).await?;
parse_openapi_response(response)
}
fn post_json_request<B>(&self, path: &str, body: &B) -> Result<HttpRequest>
where
B: Serialize + ?Sized,
{
self.json_request(HttpMethod::Post, path, body)
}
fn json_request<B>(&self, method: HttpMethod, path: &str, body: &B) -> Result<HttpRequest>
where
B: Serialize + ?Sized,
{
let url = self.config.base_url().join(path)?;
let body = serde_json::to_value(body)?;
Ok(HttpRequest::json(method, url, body))
}
}
#[cfg(test)]
mod tests;