use crate::{constants::AccessTokenType, SDKResult};
use std::{future::Future, pin::Pin};
#[derive(Debug, Clone, Default)]
pub struct TokenRequest {
pub token_type: AccessTokenType,
pub tenant_key: Option<String>,
pub app_ticket: Option<String>,
}
impl TokenRequest {
pub fn app() -> Self {
Self {
token_type: AccessTokenType::App,
..Default::default()
}
}
pub fn tenant() -> Self {
Self {
token_type: AccessTokenType::Tenant,
..Default::default()
}
}
pub fn user() -> Self {
Self {
token_type: AccessTokenType::User,
..Default::default()
}
}
pub fn tenant_key(mut self, tenant_key: impl Into<String>) -> Self {
self.tenant_key = Some(tenant_key.into());
self
}
pub fn app_ticket(mut self, app_ticket: impl Into<String>) -> Self {
self.app_ticket = Some(app_ticket.into());
self
}
}
pub trait TokenProvider: Send + Sync + std::fmt::Debug {
fn get_token(
&self,
request: TokenRequest,
) -> Pin<Box<dyn Future<Output = SDKResult<String>> + Send + '_>>;
fn get_tenant_token(
&self,
tenant_key: Option<&str>,
) -> Pin<Box<dyn Future<Output = SDKResult<String>> + Send + '_>> {
let tenant_key = tenant_key.map(str::to_owned);
Box::pin(async move {
let mut req = TokenRequest::tenant();
if let Some(key) = tenant_key.as_deref() {
if !key.is_empty() {
req = req.tenant_key(key);
}
}
self.get_token(req).await
})
}
fn get_app_token(&self) -> Pin<Box<dyn Future<Output = SDKResult<String>> + Send + '_>> {
Box::pin(async move { self.get_token(TokenRequest::app()).await })
}
fn get_user_token(&self) -> Pin<Box<dyn Future<Output = SDKResult<String>> + Send + '_>> {
Box::pin(async move { self.get_token(TokenRequest::user()).await })
}
}
#[derive(Debug)]
pub struct NoOpTokenProvider;
impl TokenProvider for NoOpTokenProvider {
fn get_token(
&self,
request: TokenRequest,
) -> Pin<Box<dyn Future<Output = SDKResult<String>> + Send + '_>> {
Box::pin(async move {
Err(crate::error::configuration_error(
format!(
"token_provider: NoOpTokenProvider 未实现 token 获取逻辑(请求:{:?}),请在 Config 中设置 TokenProvider(建议使用 openlark-auth 提供的实现)。",
request
),
))
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_token_request_app() {
let req = TokenRequest::app();
assert_eq!(req.token_type, AccessTokenType::App);
assert!(req.tenant_key.is_none());
assert!(req.app_ticket.is_none());
}
#[test]
fn test_token_request_tenant() {
let req = TokenRequest::tenant();
assert_eq!(req.token_type, AccessTokenType::Tenant);
}
#[test]
fn test_token_request_user() {
let req = TokenRequest::user();
assert_eq!(req.token_type, AccessTokenType::User);
}
#[test]
fn test_token_request_with_tenant_key() {
let req = TokenRequest::tenant().tenant_key("test_tenant");
assert_eq!(req.tenant_key, Some("test_tenant".to_string()));
}
#[test]
fn test_token_request_with_app_ticket() {
let req = TokenRequest::app().app_ticket("test_ticket");
assert_eq!(req.app_ticket, Some("test_ticket".to_string()));
}
#[test]
fn test_token_request_default() {
let req = TokenRequest::default();
assert_eq!(req.token_type, AccessTokenType::None);
}
#[test]
fn test_token_request_debug() {
let req = TokenRequest::app();
let debug_str = format!("{:?}", req);
assert!(debug_str.contains("TokenRequest"));
}
#[tokio::test]
async fn test_no_op_token_provider_returns_error() {
let provider = NoOpTokenProvider;
let result = provider.get_token(TokenRequest::app()).await;
assert!(result.is_err());
}
#[test]
fn test_no_op_token_provider_debug() {
let provider = NoOpTokenProvider;
let debug_str = format!("{:?}", provider);
assert!(debug_str.contains("NoOpTokenProvider"));
}
}