use std::hash::{Hash, Hasher};
use std::sync::Arc;
use serde_json::Value as JsonValue;
use snafu::{Snafu, ResultExt, ensure};
use ureq;
use kudu_macros::with_location;
#[derive(Clone, Debug)]
pub struct APIClient {
pub endpoint: String,
pub agent: ureq::Agent,
}
#[with_location]
#[derive(Debug, Snafu)]
pub enum HttpError {
#[snafu(display("http status: {code} - error: {message}"))]
HttpError { code: u16, message: String },
#[snafu(display("{source}"))]
ConnectionError { source: ureq::Error },
#[snafu(display("{source}"))]
JsonError { source: ureq::Error },
}
pub fn return_checked_json_response(mut response: ureq::http::Response<ureq::Body>) -> Result<JsonValue, HttpError> {
let code = response.status().as_u16();
let result: JsonValue = response
.body_mut()
.read_json().context(JsonSnafu)?;
ensure!(!(400..600).contains(&code), HttpSnafu { code, message: result["error"].to_string() });
Ok(result)
}
impl APIClient {
pub fn new(endpoint: &str) -> Self {
APIClient {
endpoint: endpoint.trim_end_matches('/').to_owned(),
agent: ureq::Agent::config_builder()
.http_status_as_error(false)
.build()
.into()
}
}
fn fullpath(&self, path: &str) -> String {
format!("{}{}", &self.endpoint, path)
}
pub fn get(&self, path: &str) -> Result<JsonValue, HttpError> {
let response = self.agent.get(self.fullpath(path))
.call().context(ConnectionSnafu)?;
return_checked_json_response(response)
}
pub fn call(&self, path: &str, params: &JsonValue) -> Result<JsonValue, HttpError> {
let response = self.agent.post(self.fullpath(path))
.send_json(params).context(ConnectionSnafu)?;
return_checked_json_response(response)
}
pub fn call_unchecked(&self, path: &str, params: &JsonValue) -> Result<JsonValue, HttpError> {
self.agent.post(self.fullpath(path))
.send_json(params).context(ConnectionSnafu)?
.body_mut()
.read_json().context(JsonSnafu)
}
pub fn local() -> Arc<Self> {
Arc::new(APIClient::new("http://127.0.0.1:8888"))
}
pub fn jungle() -> Arc<Self> {
Arc::new(APIClient::new("https://jungle4.greymass.com"))
}
pub fn vaulta() -> Arc<Self> {
Arc::new(APIClient::new("https://vaulta.greymass.com"))
}
}
impl PartialEq for APIClient {
fn eq(&self, other: &Self) -> bool {
self.endpoint == other.endpoint
}
}
impl Eq for APIClient {}
impl Hash for APIClient {
fn hash<H: Hasher>(&self, state: &mut H) {
self.endpoint.hash(state);
}
}