use serde::{Deserialize, Serialize};
use super::claims::Claims;
use super::token::RestToken;
use crate::protocol_adapters::token::ProtocolTokenError;
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "kebab-case")]
pub struct RequestRestToken {
tenant: String,
#[serde(skip_serializing_if = "Option::is_none")]
exp: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
claims: Option<Claims>,
}
impl RequestRestToken {
pub fn new(tenant: impl Into<String>) -> Self {
Self {
tenant: tenant.into(),
exp: None,
claims: None,
}
}
pub async fn send(
&self,
client: &reqwest::Client,
api_key: &str,
auth_url: &str,
) -> Result<RestToken, ProtocolTokenError> {
log::debug!("Sending request to '{}': {:?}", auth_url, self);
let response = client
.post(auth_url)
.header("apikey", api_key)
.json(self)
.send()
.await?;
let status = response.status();
let body_text = response.text().await?;
match status {
reqwest::StatusCode::OK => Ok(RestToken::parse(body_text)?),
_ => Err(ProtocolTokenError::DshCall {
url: auth_url.to_string(),
status_code: status,
error_body: body_text,
}),
}
}
pub fn tenant(&self) -> &str {
&self.tenant
}
pub fn set_exp(mut self, exp: i64) -> Self {
self.exp = Some(exp);
self
}
pub fn exp(&self) -> Option<i64> {
self.exp
}
pub fn set_claims(mut self, claims: impl Into<Claims>) -> Self {
self.claims = Some(claims.into());
self
}
pub fn claims(&self) -> Option<&Claims> {
self.claims.as_ref()
}
pub fn client_id(&self) -> Option<&str> {
self.claims.as_ref().and_then(|c| c.mqtt_token_claim().id())
}
}
impl PartialEq for RequestRestToken {
fn eq(&self, other: &Self) -> bool {
self.tenant == other.tenant && self.claims == other.claims
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::protocol_adapters::token::rest_token::DatastreamsMqttTokenClaim;
use mockito::Matcher;
use serde_json::json;
#[test]
fn test_rest_token_request() {
let request = RequestRestToken::new("test-tenant");
assert_eq!(request.tenant(), "test-tenant");
assert_eq!(request.exp(), None);
assert_eq!(request.claims(), None);
let claims: Claims = DatastreamsMqttTokenClaim::new().set_exp(1).into();
let request = request.set_exp(100).set_claims(claims.clone());
let request = request;
assert_eq!(request.exp(), Some(100));
assert_eq!(request.claims(), Some(&claims))
}
#[tokio::test]
async fn test_send_success() {
let mut mockito_server = mockito::Server::new_async().await;
let _m = mockito_server
.mock("POST", "/protocol_auth_url")
.match_header("apikey", "test_token")
.match_body(Matcher::Json(json!({"tenant": "test_tenant"})))
.with_status(200)
.with_body("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJTdHJpbmciLCJnZW4iOjEsImV4cCI6MjE0NzQ4MzY0NywidGVuYW50LWlkIjoidGVzdF90ZW5hbnQiLCJlbmRwb2ludCI6InRlc3RfZW5wb2ludCIsImNsYWltcyI6eyJkYXRhc3RyZWFtcy92MC9tcXR0L3Rva2VuIjp7fX19.Eh2-UBOgame_cQw5iHjc19-hRZXAPxMYlCHVCwcE8CU")
.create();
let client = reqwest::Client::new();
let request = RequestRestToken::new("test_tenant");
let result = request
.send(
&client,
"test_token",
&format!("{}/protocol_auth_url", mockito_server.url()),
)
.await;
assert!(result.is_ok());
let token = result.unwrap();
assert!(token.is_valid());
}
}