use std::sync::Arc;
use async_trait::async_trait;
use openapp_sdk_common::ApiKey;
use reqwest::header::{AUTHORIZATION, HeaderName};
use crate::error::SdkError;
pub const API_KEY_HEADER: HeaderName = HeaderName::from_static("x-api-key");
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AuthToken {
pub header: HeaderName,
pub value: String,
}
impl AuthToken {
#[must_use]
pub fn api_key(token: impl Into<String>) -> Self {
Self {
header: API_KEY_HEADER,
value: token.into(),
}
}
#[must_use]
pub fn bearer(token: impl AsRef<str>) -> Self {
Self {
header: AUTHORIZATION,
value: format!("Bearer {}", token.as_ref()),
}
}
}
#[async_trait]
pub trait TokenProvider: Send + Sync + std::fmt::Debug {
async fn token(&self) -> Result<AuthToken, SdkError>;
}
pub type SharedTokenProvider = Arc<dyn TokenProvider>;
#[derive(Debug, Clone)]
pub struct StaticApiKey {
key: ApiKey,
}
impl StaticApiKey {
#[must_use]
pub fn new(key: ApiKey) -> Self {
Self { key }
}
pub fn from_raw(token: impl Into<String>) -> Result<Self, SdkError> {
Ok(Self::new(ApiKey::parse(token)?))
}
#[must_use]
pub fn api_key(&self) -> &ApiKey {
&self.key
}
}
#[async_trait]
impl TokenProvider for StaticApiKey {
async fn token(&self) -> Result<AuthToken, SdkError> {
Ok(AuthToken::api_key(self.key.as_str()))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn static_provider_emits_x_api_key_with_full_token() {
let provider = StaticApiKey::from_raw("https://openapp.house_openapp_SECRET").unwrap();
let token = provider.token().await.unwrap();
assert_eq!(token.header.as_str(), "x-api-key");
assert_eq!(token.value, "https://openapp.house_openapp_SECRET");
}
#[test]
fn bearer_token_uses_authorization_header() {
let token = AuthToken::bearer("jwt.payload.sig");
assert_eq!(token.header, AUTHORIZATION);
assert_eq!(token.value, "Bearer jwt.payload.sig");
}
}