kiteconnect_async_wasm/models/auth/
session.rs1use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct SessionData {
12 pub user_id: String,
14
15 pub user_name: String,
17
18 pub user_shortname: String,
20
21 pub email: String,
23
24 pub user_type: String,
26
27 pub broker: String,
29
30 pub exchanges: Vec<String>,
32
33 pub products: Vec<String>,
35
36 pub order_types: Vec<String>,
38
39 pub api_key: String,
41
42 pub access_token: String,
46
47 #[serde(default)]
49 pub public_token: String,
50
51 #[serde(default)]
53 pub refresh_token: String,
54
55 pub login_time: String,
57
58 #[serde(default)]
60 pub meta: Option<SessionMeta>,
61
62 #[serde(default)]
64 pub avatar_url: Option<String>,
65}
66
67#[derive(Debug, Clone, Serialize, Deserialize)]
69pub struct SessionMeta {
70 #[serde(default)]
72 pub demat_consent: String,
73}
74
75impl SessionData {
76 pub fn is_valid(&self) -> bool {
78 !self.access_token.is_empty() && !self.user_id.is_empty()
79 }
80
81 pub fn token(&self) -> &str {
83 &self.access_token
84 }
85
86 pub fn has_exchange(&self, exchange: &str) -> bool {
88 self.exchanges.iter().any(|e| e == exchange)
89 }
90
91 pub fn has_product(&self, product: &str) -> bool {
93 self.products.iter().any(|p| p == product)
94 }
95
96 pub fn has_order_type(&self, order_type: &str) -> bool {
98 self.order_types.iter().any(|o| o == order_type)
99 }
100}
101
102#[derive(Debug, Clone)]
104pub struct LoginUrlConfig {
105 pub base_url: String,
107
108 pub api_key: String,
110
111 pub redirect_url: Option<String>,
113
114 pub state: Option<String>,
116}
117
118impl LoginUrlConfig {
119 pub fn new(api_key: impl Into<String>) -> Self {
121 Self {
122 base_url: "https://kite.trade/connect/login".to_string(),
123 api_key: api_key.into(),
124 redirect_url: None,
125 state: None,
126 }
127 }
128
129 pub fn with_redirect_url(mut self, url: impl Into<String>) -> Self {
131 self.redirect_url = Some(url.into());
132 self
133 }
134
135 pub fn with_state(mut self, state: impl Into<String>) -> Self {
137 self.state = Some(state.into());
138 self
139 }
140
141 pub fn build_url(&self) -> crate::models::common::KiteResult<String> {
143 use url::Url;
144
145 let mut url = Url::parse(&self.base_url)?;
146
147 url.query_pairs_mut()
149 .append_pair("api_key", &self.api_key)
150 .append_pair("v", "3"); if let Some(ref redirect_url) = self.redirect_url {
154 url.query_pairs_mut().append_pair("redirect_url", redirect_url);
155 }
156
157 if let Some(ref state) = self.state {
158 url.query_pairs_mut().append_pair("state", state);
159 }
160
161 Ok(url.to_string())
162 }
163}
164
165#[derive(Debug, Clone, Serialize, Deserialize)]
167pub struct RequestToken {
168 pub request_token: String,
170
171 #[serde(default)]
173 pub state: Option<String>,
174
175 #[serde(default)]
177 pub action: Option<String>,
178
179 #[serde(default)]
181 pub status: Option<String>,
182}
183
184impl RequestToken {
185 pub fn new(token: impl Into<String>) -> Self {
187 Self {
188 request_token: token.into(),
189 state: None,
190 action: None,
191 status: None,
192 }
193 }
194
195 pub fn is_valid(&self) -> bool {
197 !self.request_token.is_empty() && self.request_token.len() >= 10
198 }
199}
200
201#[derive(Debug, Clone, Serialize, Deserialize)]
203pub struct LogoutResponse {
204 pub success: bool,
206
207 #[serde(default)]
209 pub message: String,
210}
211
212#[cfg(test)]
213mod tests {
214 use super::*;
215
216 #[test]
217 fn test_session_data_validation() {
218 let mut session = SessionData {
219 user_id: "test_user".to_string(),
220 user_name: "Test User".to_string(),
221 user_shortname: "testuser".to_string(),
222 email: "test@example.com".to_string(),
223 user_type: "individual".to_string(),
224 broker: "ZERODHA".to_string(),
225 exchanges: vec!["NSE".to_string(), "BSE".to_string()],
226 products: vec!["CNC".to_string(), "MIS".to_string()],
227 order_types: vec!["MARKET".to_string(), "LIMIT".to_string()],
228 api_key: "test_key".to_string(),
229 access_token: "test_token".to_string(),
230 public_token: String::new(),
231 refresh_token: String::new(),
232 login_time: "2024-01-01 10:00:00".to_string(),
233 meta: None,
234 avatar_url: None,
235 };
236
237 assert!(session.is_valid());
238 assert!(session.has_exchange("NSE"));
239 assert!(!session.has_exchange("MCX"));
240 assert!(session.has_product("CNC"));
241 assert!(session.has_order_type("MARKET"));
242
243 session.access_token.clear();
245 assert!(!session.is_valid());
246 }
247
248 #[test]
249 fn test_login_url_config() {
250 let config = LoginUrlConfig::new("test_api_key")
251 .with_redirect_url("https://example.com/callback")
252 .with_state("random_state");
253
254 let url = config.build_url().expect("Failed to build URL");
255 println!("Generated URL: {}", url);
256
257 assert!(url.contains("api_key=test_api_key"));
258 assert!(url.contains("v=3"));
259 assert!(url.contains("redirect_url=") && url.contains("example.com"));
261 assert!(url.contains("state=random_state"));
262 }
263
264 #[test]
265 fn test_request_token_validation() {
266 let valid_token = RequestToken::new("abcdef1234567890");
267 assert!(valid_token.is_valid());
268
269 let invalid_token = RequestToken::new("short");
270 assert!(!invalid_token.is_valid());
271
272 let empty_token = RequestToken::new("");
273 assert!(!empty_token.is_valid());
274 }
275}