use reqwest::{
blocking,
header::{HeaderMap, HeaderValue, ACCEPT, CONTENT_LENGTH, CONTENT_TYPE},
Url,
};
use serde::{Deserialize, Serialize};
use crate::{
api::{identity::IDENTITY_API_URL, map_api_error},
error::{Result, SeaplaneError},
};
static TOKEN_API_BASE_PATH: &str = "v1/token";
#[derive(Deserialize, Serialize, Debug, Clone)]
#[cfg_attr(feature = "api_tests", derive(PartialEq))]
pub struct AccessToken {
pub token: String,
pub tenant: String,
pub subdomain: String,
}
#[derive(Default, Debug)]
pub struct TokenRequestBuilder {
api_key: Option<String>,
#[doc(hidden)]
base_url: Option<Url>,
#[cfg(any(feature = "allow_insecure_urls", feature = "danger_zone"))]
allow_http: bool,
#[cfg(any(feature = "allow_invalid_certs", feature = "danger_zone"))]
allow_invalid_certs: bool,
}
impl TokenRequestBuilder {
pub fn new() -> Self { Self::default() }
#[must_use]
pub fn api_key<S: Into<String>>(mut self, key: S) -> Self {
self.api_key = Some(key.into());
self
}
#[cfg(any(feature = "allow_insecure_urls", feature = "danger_zone"))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "allow_insecure_urls", feature = "danger_zone"))))]
pub fn allow_http(mut self, yes: bool) -> Self {
self.allow_http = yes;
self
}
#[cfg(any(feature = "allow_invalid_certs", feature = "danger_zone"))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "allow_invalid_certs", feature = "danger_zone"))))]
pub fn allow_invalid_certs(mut self, yes: bool) -> Self {
self.allow_invalid_certs = yes;
self
}
pub fn build(self) -> Result<TokenRequest> {
if self.api_key.is_none() {
return Err(SeaplaneError::MissingRequestApiKey);
}
let mut headers = HeaderMap::new();
headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
headers.insert(CONTENT_LENGTH, HeaderValue::from_static("0"));
#[cfg_attr(
not(any(
feature = "api_tests",
feature = "allow_insecure_urls",
feature = "danger_zone"
)),
allow(unused_mut)
)]
let mut builder = blocking::Client::builder()
.default_headers(headers)
.https_only(true);
cfg_if::cfg_if! {
if #[cfg(feature = "api_tests")] {
builder = builder.https_only(false);
} else if #[cfg(any(feature = "allow_insecure_urls", feature = "danger_zone"))] {
builder = builder.https_only(!self.allow_http);
}
}
#[cfg(any(feature = "allow_invalid_certs", feature = "danger_zone"))]
{
builder = builder.danger_accept_invalid_certs(self.allow_invalid_certs);
}
let url = if let Some(url) = self.base_url {
url.join(TOKEN_API_BASE_PATH)?
} else {
let mut url: Url = IDENTITY_API_URL.parse()?;
url.set_path(TOKEN_API_BASE_PATH);
url
};
Ok(TokenRequest {
api_key: self.api_key.unwrap(),
client: builder.build()?,
endpoint_url: url,
})
}
#[doc(hidden)]
pub fn base_url<S: AsRef<str>>(mut self, url: S) -> Self {
self.base_url = Some(url.as_ref().parse().unwrap());
self
}
}
#[derive(Debug)]
pub struct TokenRequest {
api_key: String,
#[doc(hidden)]
client: reqwest::blocking::Client,
#[doc(hidden)]
endpoint_url: Url,
}
impl TokenRequest {
pub fn builder() -> TokenRequestBuilder { TokenRequestBuilder::new() }
pub fn access_token(&self) -> Result<String> {
let resp = self
.client
.post(self.endpoint_url.clone())
.bearer_auth(&self.api_key)
.send()?;
map_api_error(resp)?.text().map_err(Into::into)
}
pub fn access_token_json(&self) -> Result<AccessToken> {
let resp = self
.client
.post(self.endpoint_url.clone())
.bearer_auth(&self.api_key)
.header(ACCEPT, HeaderValue::from_static("application/json"))
.send()?;
map_api_error(resp)?
.json::<AccessToken>()
.map_err(Into::into)
}
}