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()
155 .append_pair("redirect_url", redirect_url);
156 }
157
158 if let Some(ref state) = self.state {
159 url.query_pairs_mut().append_pair("state", state);
160 }
161
162 Ok(url.to_string())
163 }
164}
165
166#[derive(Debug, Clone, Serialize, Deserialize)]
168pub struct RequestToken {
169 pub request_token: String,
171
172 #[serde(default)]
174 pub state: Option<String>,
175
176 #[serde(default)]
178 pub action: Option<String>,
179
180 #[serde(default)]
182 pub status: Option<String>,
183}
184
185impl RequestToken {
186 pub fn new(token: impl Into<String>) -> Self {
188 Self {
189 request_token: token.into(),
190 state: None,
191 action: None,
192 status: None,
193 }
194 }
195
196 pub fn is_valid(&self) -> bool {
198 !self.request_token.is_empty() && self.request_token.len() >= 10
199 }
200}
201
202#[derive(Debug, Clone, Serialize, Deserialize)]
204pub struct LogoutResponse {
205 pub success: bool,
207
208 #[serde(default)]
210 pub message: String,
211}
212
213#[cfg(test)]
214mod tests {
215 use super::*;
216
217 #[test]
218 fn test_session_data_validation() {
219 let mut session = SessionData {
220 user_id: "test_user".to_string(),
221 user_name: "Test User".to_string(),
222 user_shortname: "testuser".to_string(),
223 email: "test@example.com".to_string(),
224 user_type: "individual".to_string(),
225 broker: "ZERODHA".to_string(),
226 exchanges: vec!["NSE".to_string(), "BSE".to_string()],
227 products: vec!["CNC".to_string(), "MIS".to_string()],
228 order_types: vec!["MARKET".to_string(), "LIMIT".to_string()],
229 api_key: "test_key".to_string(),
230 access_token: "test_token".to_string(),
231 public_token: String::new(),
232 refresh_token: String::new(),
233 login_time: "2024-01-01 10:00:00".to_string(),
234 meta: None,
235 avatar_url: None,
236 };
237
238 assert!(session.is_valid());
239 assert!(session.has_exchange("NSE"));
240 assert!(!session.has_exchange("MCX"));
241 assert!(session.has_product("CNC"));
242 assert!(session.has_order_type("MARKET"));
243
244 session.access_token.clear();
246 assert!(!session.is_valid());
247 }
248
249 #[test]
250 fn test_login_url_config() {
251 let config = LoginUrlConfig::new("test_api_key")
252 .with_redirect_url("https://example.com/callback")
253 .with_state("random_state");
254
255 let url = config.build_url().expect("Failed to build URL");
256 println!("Generated URL: {}", url);
257
258 assert!(url.contains("api_key=test_api_key"));
259 assert!(url.contains("v=3"));
260 assert!(url.contains("redirect_url=") && url.contains("example.com"));
262 assert!(url.contains("state=random_state"));
263 }
264
265 #[test]
266 fn test_request_token_validation() {
267 let valid_token = RequestToken::new("abcdef1234567890");
268 assert!(valid_token.is_valid());
269
270 let invalid_token = RequestToken::new("short");
271 assert!(!invalid_token.is_valid());
272
273 let empty_token = RequestToken::new("");
274 assert!(!empty_token.is_valid());
275 }
276}