use async_trait::async_trait;
use crate::api::rest_endpoint::prepare_request;
use crate::api::{ApiError, RestEndpoint};
#[cfg(feature = "async")]
use crate::api::{AsyncClient, QueryAsync};
#[cfg(feature = "sync")]
use crate::api::{Client, Query};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Ignore<E> {
endpoint: E,
}
pub fn ignore<E>(endpoint: E) -> Ignore<E> {
Ignore { endpoint }
}
#[cfg(feature = "sync")]
impl<E, C> Query<(), C> for Ignore<E>
where
E: RestEndpoint,
C: Client,
{
fn query(&self, client: &C) -> Result<(), ApiError<C::Error>> {
let ep = client.get_service_endpoint(
&self.endpoint.service_type(),
self.endpoint.api_version().as_ref(),
)?;
let (req, data) = prepare_request::<C, E>(
ep,
ep.build_request_url(&self.endpoint.endpoint())?,
&self.endpoint,
)?;
let query_uri = req.uri_ref().cloned();
let rsp = client.rest(req, data)?;
let status = rsp.status();
if !status.is_success() {
let v = if let Ok(v) = serde_json::from_slice(rsp.body()) {
v
} else {
return Err(ApiError::server_error(query_uri, &rsp, rsp.body()));
};
return Err(ApiError::from_openstack(query_uri, &rsp, v));
}
Ok(())
}
}
#[cfg(feature = "async")]
#[async_trait]
impl<E, C> QueryAsync<(), C> for Ignore<E>
where
E: RestEndpoint + Sync,
C: AsyncClient + Sync,
{
async fn query_async(&self, client: &C) -> Result<(), ApiError<C::Error>> {
let ep = client.get_service_endpoint(
&self.endpoint.service_type(),
self.endpoint.api_version().as_ref(),
)?;
let (req, data) = prepare_request::<C, E>(
ep,
ep.build_request_url(&self.endpoint.endpoint())?,
&self.endpoint,
)?;
let query_uri = req.uri_ref().cloned();
let rsp = client.rest_async(req, data).await?;
let status = rsp.status();
if !status.is_success() {
let v = if let Ok(v) = serde_json::from_slice(rsp.body()) {
v
} else {
return Err(ApiError::server_error(query_uri, &rsp, rsp.body()));
};
return Err(ApiError::from_openstack(query_uri, &rsp, v));
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use http::StatusCode;
use httpmock::MockServer;
use serde_json::json;
use crate::api::rest_endpoint_prelude::*;
#[cfg(feature = "sync")]
use crate::api::Query;
#[cfg(feature = "async")]
use crate::api::QueryAsync;
use crate::api::{self, ApiError};
use crate::test::client::FakeOpenStackClient;
use crate::types::ServiceType;
struct Dummy;
impl RestEndpoint for Dummy {
fn method(&self) -> http::Method {
http::Method::GET
}
fn endpoint(&self) -> Cow<'static, str> {
"dummy".into()
}
fn service_type(&self) -> ServiceType {
ServiceType::from("dummy")
}
}
#[cfg(feature = "sync")]
#[test]
fn test_openstack_non_json_response() {
let server = MockServer::start();
let client = FakeOpenStackClient::new(server.base_url());
let mock = server.mock(|when, then| {
when.method(httpmock::Method::GET).path("/dummy");
then.status(StatusCode::OK).body("not json");
});
api::ignore(Dummy).query(&client).unwrap();
mock.assert();
}
#[cfg(feature = "async")]
#[tokio::test]
async fn test_openstack_non_json_response_async() {
let server = MockServer::start_async().await;
let client = FakeOpenStackClient::new(server.base_url());
let mock = server.mock(|when, then| {
when.method(httpmock::Method::GET).path("/dummy");
then.status(StatusCode::OK).body("not json");
});
api::ignore(Dummy).query_async(&client).await.unwrap();
mock.assert();
}
#[cfg(feature = "sync")]
#[test]
fn test_openstack_error_bad_json() {
let server = MockServer::start();
let client = FakeOpenStackClient::new(server.base_url());
let mock = server.mock(|when, then| {
when.method(httpmock::Method::GET).path("/dummy");
then.status(StatusCode::CONFLICT);
});
let err = api::ignore(Dummy).query(&client).unwrap_err();
if let ApiError::OpenStackService { status, .. } = err {
assert_eq!(status, http::StatusCode::CONFLICT);
} else {
panic!("unexpected error: {err}");
}
mock.assert();
}
#[cfg(feature = "async")]
#[tokio::test]
async fn test_openstack_error_bad_json_async() {
let server = MockServer::start_async().await;
let client = FakeOpenStackClient::new(server.base_url());
let mock = server.mock(|when, then| {
when.method(httpmock::Method::GET).path("/dummy");
then.status(StatusCode::CONFLICT);
});
let err = api::ignore(Dummy).query_async(&client).await.unwrap_err();
if let ApiError::OpenStackService { status, .. } = err {
assert_eq!(status, http::StatusCode::CONFLICT);
} else {
panic!("unexpected error: {err}");
}
mock.assert();
}
#[cfg(feature = "sync")]
#[test]
fn test_openstack_error_detection() {
let server = MockServer::start();
let client = FakeOpenStackClient::new(server.base_url());
let mock = server.mock(|when, then| {
when.method(httpmock::Method::GET).path("/dummy");
then.status(StatusCode::CONFLICT)
.json_body(json!({"message": "dummy error message"}));
});
let err = api::ignore(Dummy).query(&client).unwrap_err();
if let ApiError::OpenStack { msg, .. } = err {
assert_eq!(msg, "dummy error message");
} else {
panic!("unexpected error: {err}");
}
mock.assert();
}
#[cfg(feature = "async")]
#[tokio::test]
async fn test_openstack_error_detection_async() {
let server = MockServer::start_async().await;
let client = FakeOpenStackClient::new(server.base_url());
let mock = server.mock(|when, then| {
when.method(httpmock::Method::GET).path("/dummy");
then.status(StatusCode::CONFLICT)
.json_body(json!({"message": "dummy error message"}));
});
let err = api::ignore(Dummy).query_async(&client).await.unwrap_err();
if let ApiError::OpenStack { msg, .. } = err {
assert_eq!(msg, "dummy error message");
} else {
panic!("unexpected error: {err}");
}
mock.assert();
}
#[cfg(feature = "sync")]
#[test]
fn test_openstack_error_detection_unknown() {
let server = MockServer::start();
let client = FakeOpenStackClient::new(server.base_url());
let err_obj = json!({"bogus": "dummy error message"});
let mock = server.mock(|when, then| {
when.method(httpmock::Method::GET).path("/dummy");
then.status(StatusCode::CONFLICT).json_body(err_obj.clone());
});
let err = api::ignore(Dummy).query(&client).unwrap_err();
if let ApiError::OpenStackUnrecognized { obj, .. } = err {
assert_eq!(obj, err_obj);
} else {
panic!("unexpected error: {err}");
}
mock.assert();
}
#[cfg(feature = "async")]
#[tokio::test]
async fn test_openstack_error_detection_unknown_async() {
let server = MockServer::start_async().await;
let client = FakeOpenStackClient::new(server.base_url());
let err_obj = json!({"bogus": "dummy error message"});
let mock = server.mock(|when, then| {
when.method(httpmock::Method::GET).path("/dummy");
then.status(StatusCode::CONFLICT).json_body(err_obj.clone());
});
let err = api::ignore(Dummy).query_async(&client).await.unwrap_err();
if let ApiError::OpenStackUnrecognized { obj, .. } = err {
assert_eq!(obj, err_obj);
} else {
panic!("unexpected error: {err}");
}
mock.assert();
}
}