use chrono::{DateTime, Utc};
use openlark_core::api::responses::ApiResponseTrait;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct AccessTokenResponse {
pub app_access_token: String,
pub expires_in: u64,
pub tenant_key: String,
pub token_type: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct TenantAccessTokenResponse {
pub tenant_access_token: String,
pub expires_in: u64,
pub token_type: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct AppTicketResponse {
pub app_ticket: String,
pub success: bool,
pub error_message: Option<String>,
}
impl ApiResponseTrait for AccessTokenResponse {}
impl ApiResponseTrait for TenantAccessTokenResponse {}
impl ApiResponseTrait for AppTicketResponse {}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AppAccessTokenInternalRequest {
pub app_id: String,
pub app_secret: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AppAccessTokenRequest {
pub app_id: String,
pub app_secret: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TenantAccessTokenInternalRequest {
pub app_id: String,
pub app_secret: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TenantAccessTokenRequest {
pub app_id: String,
pub app_secret: String,
pub app_ticket: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AppTicketResendRequest {
pub app_id: String,
pub app_secret: String,
}
#[derive(Debug, Clone)]
pub struct TokenInfo {
pub access_token: String,
pub expires_at: DateTime<Utc>,
pub token_type: String,
pub created_at: DateTime<Utc>,
}
impl TokenInfo {
pub fn new(access_token: String, expires_in: u64, token_type: String) -> Self {
let now = Utc::now();
let expires_at = now + chrono::Duration::seconds(expires_in as i64);
Self {
access_token,
expires_at,
token_type,
created_at: now,
}
}
pub fn is_expired(&self) -> bool {
Utc::now() >= self.expires_at
}
pub fn is_expiring_soon(&self) -> bool {
let soon = Utc::now() + chrono::Duration::minutes(5);
soon >= self.expires_at
}
pub fn remaining_seconds(&self) -> i64 {
(self.expires_at - Utc::now()).num_seconds().max(0)
}
}
#[cfg(test)]
#[allow(unused_imports)]
mod tests {
use super::*;
#[test]
fn test_access_token_response_serialization() {
let response = AccessTokenResponse {
app_access_token: "test_token".to_string(),
expires_in: 3600,
tenant_key: "test_tenant".to_string(),
token_type: Some("Bearer".to_string()),
};
let json = serde_json::to_string(&response).unwrap();
assert!(json.contains("test_token"));
assert!(json.contains("3600"));
assert!(json.contains("test_tenant"));
}
#[test]
fn test_token_info() {
let token = TokenInfo::new("test_token".to_string(), 3600, "Bearer".to_string());
assert_eq!(token.access_token, "test_token");
assert_eq!(token.token_type, "Bearer");
assert!(!token.is_expired());
assert!(token.remaining_seconds() > 0);
}
#[test]
fn test_expiring_token() {
let mut token = TokenInfo::new("test_token".to_string(), 1, "Bearer".to_string());
token.expires_at = Utc::now() - chrono::Duration::seconds(1);
assert!(token.is_expired());
assert!(token.is_expiring_soon());
assert_eq!(token.remaining_seconds(), 0);
}
}