use std::collections::HashMap;
use base64::Engine as _;
use http_body_util::BodyExt;
use hyper;
use hyper::header::{HeaderValue, AUTHORIZATION, CONTENT_LENGTH, CONTENT_TYPE, USER_AGENT};
use hyper_util::client::legacy::connect::Connect;
use serde;
use serde_json;
use super::{configuration, Error};
pub(crate) struct ApiKey {
pub in_header: bool,
pub in_query: bool,
pub param_name: String,
}
impl ApiKey {
fn key(&self, prefix: &Option<String>, key: &str) -> String {
match prefix {
None => key.to_owned(),
Some(ref prefix) => format!("{} {}", prefix, key),
}
}
}
#[allow(dead_code)]
pub(crate) enum Auth {
None,
ApiKey(ApiKey),
Basic,
Oauth,
}
pub(crate) struct Request {
auth: Option<Auth>,
method: hyper::Method,
path: String,
query_params: HashMap<String, String>,
no_return_type: bool,
path_params: HashMap<String, String>,
form_params: HashMap<String, String>,
header_params: HashMap<String, String>,
serialized_body: Option<String>,
}
#[allow(dead_code)]
impl Request {
pub fn new(method: hyper::Method, path: String) -> Self {
Request {
auth: None,
method,
path,
query_params: HashMap::new(),
path_params: HashMap::new(),
form_params: HashMap::new(),
header_params: HashMap::new(),
serialized_body: None,
no_return_type: false,
}
}
pub fn with_body_param<T: serde::Serialize>(mut self, param: T) -> Self {
self.serialized_body = Some(serde_json::to_string(¶m).unwrap());
self
}
pub fn with_header_param(mut self, basename: String, param: String) -> Self {
self.header_params.insert(basename, param);
self
}
#[allow(unused)]
pub fn with_query_param(mut self, basename: String, param: String) -> Self {
self.query_params.insert(basename, param);
self
}
#[allow(unused)]
pub fn with_path_param(mut self, basename: String, param: String) -> Self {
self.path_params.insert(basename, param);
self
}
#[allow(unused)]
pub fn with_form_param(mut self, basename: String, param: String) -> Self {
self.form_params.insert(basename, param);
self
}
pub fn returns_nothing(mut self) -> Self {
self.no_return_type = true;
self
}
pub fn with_auth(mut self, auth: Auth) -> Self {
self.auth = Some(auth);
self
}
pub async fn execute<C, U>(self, conf: &configuration::Configuration<C>) -> Result<U, Error>
where
C: Connect + Clone + std::marker::Send + Sync,
U: Sized + std::marker::Send,
for<'de> U: serde::Deserialize<'de>,
{
let mut path = self.path;
for (k, v) in self.path_params {
path = path.replace(&("{".to_owned() + &k + "}"), &v);
}
let mut query_params: Vec<(String, String)> = self.query_params.into_iter().collect();
let auth = self.auth.unwrap_or_else(||
if conf.api_key.is_some() {
panic!("Cannot automatically set the API key from the configuration, it must be specified in the OpenAPI definition")
} else if conf.oauth_access_token.is_some() {
Auth::Oauth
} else if conf.basic_auth.is_some() {
Auth::Basic
} else {
Auth::None
}
);
let mut req_builder = {
let mut uri_str = format!("{}{}", conf.base_path, path);
if let Auth::ApiKey(ref apikey) = auth {
if let Some(ref key) = conf.api_key {
if apikey.in_query {
let val = apikey.key(&key.prefix, &key.key);
query_params.push((apikey.param_name.clone(), val));
}
}
}
if !query_params.is_empty() {
let query_string_str = ::url::form_urlencoded::Serializer::new("".to_owned())
.extend_pairs(&query_params)
.finish();
uri_str += "?";
uri_str += &query_string_str;
}
let uri: hyper::Uri = uri_str.parse().map_err(Error::UriError)?;
hyper::Request::builder().uri(uri).method(self.method)
};
match auth {
Auth::ApiKey(apikey) => {
if let Some(ref key) = conf.api_key {
if apikey.in_header {
let val = apikey.key(&key.prefix, &key.key);
req_builder = req_builder.header(&apikey.param_name, val);
}
}
}
Auth::Basic => {
if let Some(ref auth_conf) = conf.basic_auth {
let mut text = auth_conf.0.clone();
text.push(':');
if let Some(ref pass) = auth_conf.1 {
text.push_str(&pass[..]);
}
let encoded = base64::engine::general_purpose::STANDARD.encode(&text);
req_builder = req_builder.header(AUTHORIZATION, encoded);
}
}
Auth::Oauth => {
if let Some(ref token) = conf.oauth_access_token {
let text = "Bearer ".to_owned() + token;
req_builder = req_builder.header(AUTHORIZATION, text);
}
}
Auth::None => {}
}
if let Some(ref user_agent) = conf.user_agent {
req_builder = req_builder.header(
USER_AGENT,
HeaderValue::from_str(user_agent).map_err(super::Error::Header)?,
);
}
for (k, v) in self.header_params {
req_builder = req_builder.header(&k, v);
}
let req_headers = req_builder.headers_mut().unwrap();
let request_result = if !self.form_params.is_empty() {
req_headers.insert(
CONTENT_TYPE,
HeaderValue::from_static("application/x-www-form-urlencoded"),
);
let mut enc = ::url::form_urlencoded::Serializer::new("".to_owned());
for (k, v) in self.form_params {
enc.append_pair(&k, &v);
}
req_builder.body(enc.finish())
} else if let Some(body) = self.serialized_body {
req_headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
req_headers.insert(CONTENT_LENGTH, body.len().into());
req_builder.body(body)
} else {
req_builder.body(String::new())
};
let request = request_result.map_err(Error::from)?;
let no_return_type = self.no_return_type;
let timeout_duration = conf.timeout;
let fut = async {
let response = conf.client.request(request).await.map_err(Error::from)?;
let status = response.status();
if !status.is_success() {
Err(Error::from((status, response.into_body())))
} else if no_return_type {
serde_json::from_str("null").map_err(Error::from)
} else {
let collected = response.into_body().collect().await.map_err(Error::from)?;
serde_json::from_slice(&collected.to_bytes()).map_err(Error::from)
}
};
match timeout_duration {
Some(d) => tokio::time::timeout(d, fut)
.await
.unwrap_or(Err(Error::Timeout)),
None => fut.await,
}
}
}