use super::{
api::Api,
http::{header, multipart, Method, Request, Response},
};
use anyhow::{bail, Result as AnyResult};
use std::{sync::Arc, time::Duration};
#[derive(Clone, Debug)]
pub struct Config {
pub base_url: String,
pub api_key: String,
pub timeout: Duration,
}
impl Default for Config {
fn default() -> Self {
Self {
base_url: "https://api.dify.ai".into(),
api_key: "API_KEY".into(),
timeout: Duration::from_secs(30),
}
}
}
#[derive(Clone, Debug)]
pub struct Client {
pub config: Arc<Config>,
http_client: reqwest::Client,
}
impl Client {
pub fn new(base_url: &str, api_key: &str) -> Self {
Self::new_with_config(Config {
base_url: base_url.into(),
api_key: api_key.into(),
..Config::default()
})
}
pub fn new_with_config(mut c: Config) -> Self {
c.base_url = c.base_url.trim_end_matches("/").into();
let mut builder = reqwest::ClientBuilder::new();
if !c.timeout.is_zero() {
builder = builder.timeout(c.timeout);
}
let http_client = builder
.default_headers(Self::default_headers(&c))
.build()
.expect("Failed to create http client");
Self {
config: Arc::new(c),
http_client,
}
}
fn default_headers(c: &Config) -> header::HeaderMap {
let mut headers = header::HeaderMap::new();
headers.insert(
header::CACHE_CONTROL,
header::HeaderValue::from_static("no-cache"),
);
headers.insert(
header::CONTENT_TYPE,
header::HeaderValue::from_static("application/json; charset=utf-8"),
);
let auth = format!("Bearer {}", c.api_key);
let mut bearer_auth = header::HeaderValue::from_str(&auth).unwrap();
bearer_auth.set_sensitive(true);
headers.insert(header::AUTHORIZATION, bearer_auth);
headers
}
pub fn api(&self) -> Api {
Api::new(self)
}
pub(crate) fn create_request<T>(
&self,
url: String,
method: Method,
data: T,
) -> AnyResult<Request>
where
T: serde::Serialize,
{
match method {
Method::POST => {
let r = self.http_client.post(url).json(&data).build()?;
Ok(r)
}
Method::GET => {
let r = self.http_client.get(url).query(&data).build()?;
Ok(r)
}
Method::DELETE => {
let r = self.http_client.delete(url).json(&data).build()?;
Ok(r)
}
_ => bail!("Method not supported"),
}
}
pub(crate) fn create_multipart_request(
&self,
url: String,
form_data: multipart::Form,
) -> AnyResult<Request> {
let r = self.http_client.post(url).multipart(form_data).build()?;
Ok(r)
}
pub(crate) async fn execute(&self, request: Request) -> AnyResult<Response> {
self.http_client.execute(request).await.map_err(Into::into)
}
}