#[cfg(feature = "async")]
use async_trait::async_trait;
use bytes::Bytes;
use http::request::Builder as RequestBuilder;
use http::Response;
use http::{HeaderMap, Response as HttpResponse};
#[cfg(feature = "sync")]
use reqwest::blocking::Client as HttpClient;
#[cfg(feature = "async")]
use reqwest::Client as AsyncHttpClient;
use std::collections::HashMap;
use url::Url;
#[cfg(feature = "async")]
use crate::api::AsyncClient;
#[cfg(feature = "sync")]
use crate::api::Client;
use crate::api::{ApiError, RestClient};
use crate::auth::Auth;
use crate::catalog::{CatalogError, ServiceEndpoint};
use crate::types::identity::v3::Project;
use crate::types::{ApiVersion, BoxedAsyncRead, ServiceType};
use crate::RestError;
pub struct FakeOpenStackClient {
endpoints: HashMap<String, ServiceEndpoint>,
auth: Option<Auth>,
}
impl FakeOpenStackClient {
#[allow(clippy::expect_used)]
pub fn new<S: AsRef<str>>(url: S) -> Self {
let mut slf = Self {
endpoints: HashMap::new(),
auth: None,
};
slf.add_endpoint(
"default",
Url::parse(url.as_ref()).expect("valid test url is used"),
);
slf
}
pub fn add_endpoint<S: AsRef<str>>(&mut self, service_type: S, url: Url) -> &mut Self {
self.endpoints.insert(
service_type.as_ref().into(),
ServiceEndpoint::new(url, ApiVersion::new(0, 0)),
);
self
}
pub fn set_auth(&mut self, auth: Option<Auth>) -> &mut Self {
self.auth = auth;
self
}
}
impl RestClient for FakeOpenStackClient {
type Error = RestError;
fn get_service_endpoint(
&self,
service_type: &ServiceType,
_version: Option<&ApiVersion>,
) -> Result<&ServiceEndpoint, ApiError<Self::Error>> {
self.endpoints
.get(&service_type.to_string())
.or(self.endpoints.get("default"))
.ok_or(ApiError::catalog(CatalogError::ServiceNotConfigured(
service_type.to_string(),
)))
}
fn get_current_project(&self) -> Option<Project> {
None
}
}
#[cfg(feature = "sync")]
impl Client for FakeOpenStackClient {
fn rest(
&self,
mut request: RequestBuilder,
body: Vec<u8>,
) -> Result<Response<Bytes>, ApiError<Self::Error>> {
let call = || -> Result<_, Self::Error> {
if let Some(auth) = &self.auth {
if let Some(headers) = request.headers_mut() {
auth.set_header(headers)?;
}
}
let http_request = request.body(body.clone())?;
let request = http_request.try_into()?;
let client = HttpClient::new();
let rsp = client.execute(request)?;
let mut http_rsp = HttpResponse::builder()
.status(rsp.status())
.version(rsp.version());
if let Some(headers) = http_rsp.headers_mut() {
headers.extend(rsp.headers().clone())
}
Ok(http_rsp.body(rsp.bytes()?)?)
};
call().map_err(ApiError::client)
}
}
#[cfg(feature = "async")]
#[async_trait]
impl AsyncClient for FakeOpenStackClient {
async fn rest_async(
&self,
mut request: http::request::Builder,
body: Vec<u8>,
) -> Result<HttpResponse<Bytes>, ApiError<Self::Error>> {
use futures_util::TryFutureExt;
let call = || async {
if let Some(auth) = &self.auth {
if let Some(headers) = request.headers_mut() {
auth.set_header(headers)?;
}
}
let http_request = request.body(body)?;
let request = http_request.try_into()?;
let client = AsyncHttpClient::new();
let rsp = client.execute(request).await?;
let mut http_rsp = HttpResponse::builder()
.status(rsp.status())
.version(rsp.version());
if let Some(headers) = http_rsp.headers_mut() {
headers.extend(rsp.headers().clone())
}
Ok(http_rsp.body(rsp.bytes().await?)?)
};
call().map_err(ApiError::client).await
}
async fn rest_read_body_async(
&self,
_request: RequestBuilder,
_body: BoxedAsyncRead,
) -> Result<Response<Bytes>, ApiError<Self::Error>> {
todo!();
}
async fn download_async(
&self,
_request: RequestBuilder,
_body: Vec<u8>,
) -> Result<(HeaderMap, BoxedAsyncRead), ApiError<Self::Error>> {
todo!();
}
}