#![allow(dead_code)]
use std::sync::Mutex;
use std::time::Duration;
use hey_sdk::http::HttpClient;
use hey_sdk::observability::{Hooks, OperationInfo, OperationState};
use hey_sdk::{Client, ClientBuilder, Config, Error, StaticTokenProvider};
use wiremock::MockServer;
pub(crate) const TOKEN: &str = "test-token";
pub(crate) fn builder(server: &MockServer) -> ClientBuilder {
Client::builder(Config::default().with_base_url(server.uri()))
.token_provider(StaticTokenProvider::new(TOKEN))
.http_client(http_client())
.base_delay(Duration::from_millis(20))
.max_delay(Duration::from_millis(20))
.max_jitter(Duration::ZERO)
}
#[cfg(feature = "reqwest")]
pub(crate) fn http_client() -> impl HttpClient + 'static {
hey_sdk::http::ReqwestClient::default()
}
#[cfg(not(feature = "reqwest"))]
pub(crate) fn http_client() -> impl HttpClient + 'static {
transport::DevTransport::default()
}
#[cfg(not(feature = "reqwest"))]
mod transport {
use async_trait::async_trait;
use bytes::Bytes;
use futures_util::stream;
use hey_sdk::Error;
use hey_sdk::http::{Body, HttpClient, Request, Response};
use reqwest::redirect::Policy;
pub(crate) struct DevTransport {
http: reqwest::Client,
}
impl Default for DevTransport {
fn default() -> DevTransport {
let http = reqwest::Client::builder()
.redirect(Policy::none())
.timeout(std::time::Duration::from_secs(30))
.build()
.expect("reqwest builds a client from its defaults");
DevTransport { http }
}
}
#[async_trait]
impl HttpClient for DevTransport {
async fn send(&self, request: Request<Bytes>) -> Result<Response<Body>, Error> {
let request = reqwest::Request::try_from(request).map_err(Error::network)?;
let answered = self.http.execute(request).await.map_err(Error::network)?;
let status = answered.status();
let version = answered.version();
let headers = answered.headers().clone();
let content_length = answered.content_length();
let chunks = stream::try_unfold(answered, |mut answered| async move {
match answered.chunk().await {
Ok(Some(chunk)) => Ok(Some((chunk, answered))),
Ok(None) => Ok(None),
Err(error) => Err(Error::network(error)),
}
});
let mut response = Response::new(Body::from_stream(chunks, content_length));
*response.status_mut() = status;
*response.version_mut() = version;
*response.headers_mut() = headers;
Ok(response)
}
}
}
pub(crate) fn client(server: &MockServer) -> Client {
builder(server).build().unwrap()
}
#[derive(Default)]
pub(crate) struct Operations {
started: Mutex<Vec<String>>,
}
impl Operations {
pub(crate) fn started(&self) -> Vec<String> {
self.started.lock().unwrap().clone()
}
}
impl Hooks for Operations {
fn on_operation_start(&self, op: &OperationInfo) -> OperationState {
self.started
.lock()
.unwrap()
.push(format!("{}.{}", op.service, op.operation));
None
}
}
#[derive(Default)]
pub(crate) struct Outcomes {
statuses: Mutex<Vec<Option<u16>>>,
}
impl Outcomes {
pub(crate) fn statuses(&self) -> Vec<Option<u16>> {
self.statuses.lock().unwrap().clone()
}
}
impl Hooks for Outcomes {
fn on_operation_end(
&self,
_op: &OperationInfo,
_state: OperationState,
outcome: Result<(), &Error>,
_duration: Duration,
) {
self.statuses
.lock()
.unwrap()
.push(outcome.err().and_then(Error::http_status));
}
}