alipan 0.0.4

alipan sdk for rust
Documentation
use crate::oauth_access_token::OauthAccessToken;
use crate::{GrantType, OAuthClient};
use async_trait::async_trait;
use serde_derive::{Deserialize, Serialize};
use std::fmt::Debug;
use std::sync::Arc;

pub type BoxedAccessTokenLoader = Box<dyn AccessTokenLoader>;

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct AccessToken {
    pub access_token: String,
    pub token_type: String,
    pub expires_in: i64,
    pub refresh_token: String,
    pub created_at: i64,
}

impl AccessToken {
    pub fn wrap_oauth_token(token: OauthAccessToken) -> Self {
        AccessToken {
            access_token: token.access_token,
            token_type: token.token_type,
            expires_in: token.expires_in,
            refresh_token: token.refresh_token,
            created_at: chrono::Utc::now().timestamp(),
        }
    }
}

#[async_trait]
pub trait AccessTokenLoader: Debug + Send + Sync {
    async fn get_access_token(&self) -> anyhow::Result<AccessToken>;
}

#[derive(Debug)]
pub struct UninitializedAccessTokenLoader;

#[async_trait]
impl AccessTokenLoader for UninitializedAccessTokenLoader {
    async fn get_access_token(&self) -> anyhow::Result<AccessToken> {
        Err(anyhow::Error::msg("uninitialized access token loader"))
    }
}

#[derive(Debug)]
pub struct OAuthClientAccessTokenManager {
    pub oauth_client: Arc<OAuthClient>,
    pub access_token_store: Arc<Box<dyn OAuthClientAccessTokenStore>>,
}

#[async_trait]
pub trait OAuthClientAccessTokenStore: Debug + Send + Sync {
    async fn get_access_token(&self) -> anyhow::Result<Option<AccessToken>>;

    async fn set_access_token(&self, access_token: AccessToken) -> anyhow::Result<()>;
}

#[async_trait]
impl AccessTokenLoader for OAuthClientAccessTokenManager {
    async fn get_access_token(&self) -> anyhow::Result<AccessToken> {
        let token = self.access_token_store.get_access_token().await?;
        let token = match token {
            Some(token) => {
                let now = chrono::Utc::now().timestamp();
                if now - token.created_at < token.expires_in * 3 / 4 {
                    return Ok(token);
                }
                token
            }
            None => return Err(anyhow::Error::msg("no access token")),
        };
        let token = self
            .oauth_client
            .oauth_access_token()
            .await
            .grant_type(GrantType::RefreshToken)
            .refresh_token(token.refresh_token.as_str())
            .request()
            .await?;
        let access_token = AccessToken::wrap_oauth_token(token);
        self.access_token_store
            .set_access_token(access_token.clone())
            .await?;
        Ok(access_token)
    }
}