use std::collections::HashMap;
use crate::core::{Credentials, ExchangeResult, ExchangeError};
#[derive(Clone)]
pub struct UpstoxAuth {
pub api_key: String,
pub api_secret: String,
pub access_token: Option<String>,
pub redirect_uri: Option<String>,
}
impl UpstoxAuth {
pub fn new(credentials: &Credentials) -> ExchangeResult<Self> {
Ok(Self {
api_key: credentials.api_key.clone(),
api_secret: credentials.api_secret.clone(),
access_token: None,
redirect_uri: None,
})
}
pub fn from_env() -> ExchangeResult<Self> {
let api_key = std::env::var("UPSTOX_API_KEY")
.map_err(|_| ExchangeError::Auth("UPSTOX_API_KEY not set".to_string()))?;
let api_secret = std::env::var("UPSTOX_API_SECRET")
.map_err(|_| ExchangeError::Auth("UPSTOX_API_SECRET not set".to_string()))?;
let access_token = std::env::var("UPSTOX_ACCESS_TOKEN").ok();
let redirect_uri = std::env::var("UPSTOX_REDIRECT_URI").ok();
Ok(Self {
api_key,
api_secret,
access_token,
redirect_uri,
})
}
pub fn _with_token(
api_key: impl Into<String>,
api_secret: impl Into<String>,
access_token: impl Into<String>,
) -> Self {
Self {
api_key: api_key.into(),
api_secret: api_secret.into(),
access_token: Some(access_token.into()),
redirect_uri: None,
}
}
pub fn _with_redirect_uri(mut self, uri: impl Into<String>) -> Self {
self.redirect_uri = Some(uri.into());
self
}
pub fn set_access_token(&mut self, token: String) {
self.access_token = Some(token);
}
pub fn get_authorization_url(&self, state: Option<&str>) -> String {
let redirect = self.redirect_uri.as_deref().unwrap_or("http://localhost");
let mut url = format!(
"https://api.upstox.com/v2/login/authorization/dialog?client_id={}&redirect_uri={}&response_type=code",
self.api_key,
urlencoding::encode(redirect)
);
if let Some(s) = state {
url.push_str(&format!("&state={}", urlencoding::encode(s)));
}
url
}
pub fn build_token_exchange_body(&self, code: &str) -> HashMap<String, String> {
let mut body = HashMap::new();
body.insert("code".to_string(), code.to_string());
body.insert("client_id".to_string(), self.api_key.clone());
body.insert("client_secret".to_string(), self.api_secret.clone());
body.insert(
"redirect_uri".to_string(),
self.redirect_uri
.as_deref()
.unwrap_or("http://localhost")
.to_string(),
);
body.insert("grant_type".to_string(), "authorization_code".to_string());
body
}
pub fn sign_headers(&self, headers: &mut HashMap<String, String>) -> ExchangeResult<()> {
if let Some(token) = &self.access_token {
headers.insert(
"Authorization".to_string(),
format!("Bearer {}", token),
);
headers.insert("Accept".to_string(), "application/json".to_string());
Ok(())
} else {
Err(ExchangeError::Auth(
"No access token available. Please authenticate first.".to_string(),
))
}
}
pub fn _has_token(&self) -> bool {
self.access_token.is_some()
}
pub fn _api_key(&self) -> &str {
&self.api_key
}
pub fn _api_secret(&self) -> &str {
&self.api_secret
}
}