Skip to main content

kiteconnect_rs/
http.rs

1use reqwest::{
2    Method, Response,
3    header::{HeaderMap, HeaderValue},
4};
5use serde::{
6    Deserialize, Serialize,
7    de::{DeserializeOwned, Error},
8};
9use std::collections::HashMap;
10
11use crate::{
12    KiteConnect,
13    KiteConnectErrorKind::SerializationError,
14    constants::app_constants::*,
15    models::{KiteConnectError, KiteError},
16};
17
18#[derive(Debug, Clone, Serialize, Deserialize)]
19struct ApiResponse<T> {
20    status: String,
21    data: T,
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize)]
25struct DataOnlyResponse<T> {
26    data: T,
27}
28
29pub enum RequestBody<T: Serialize> {
30    Form(T),
31    Json(T),
32}
33
34impl KiteConnect {
35    /// Central method for making authenticated API requests
36    async fn do_envelope<T, K: Serialize>(
37        &self,
38        method: Method,
39        endpoint: &str,
40        query_params: Option<HashMap<String, String>>,
41        body: Option<RequestBody<K>>,
42        headers: Option<HeaderMap>,
43    ) -> Result<T, KiteConnectError>
44    where
45        T: DeserializeOwned,
46    {
47        let url = format!("{}{}", self.base_url, endpoint);
48        let mut request_headers = self.get_default_headers()?;
49
50        // Add Authorization header if access token is available
51        if let Some(ref token) = self.access_token {
52            request_headers.insert(
53                "Authorization",
54                HeaderValue::from_str(&format!("token {}:{}", self.api_key, token))?,
55            );
56        }
57
58        // Merge custom headers if provided
59        if let Some(custom_headers) = headers {
60            for (key, value) in custom_headers.iter() {
61                request_headers.insert(key, value.clone());
62            }
63        }
64
65        let mut request_builder = self
66            .http_client
67            .request(method, &url)
68            .headers(request_headers);
69
70        // Handle query parameters if present
71        if let Some(query) = query_params {
72            request_builder = request_builder.query(&query);
73        }
74
75        // Handle request body if present
76        if let Some(body) = body {
77            match body {
78                RequestBody::Form(form_params) => {
79                    request_builder = request_builder.form(&form_params);
80                }
81                RequestBody::Json(json_body) => {
82                    request_builder = request_builder.json(&json_body);
83                }
84            }
85        }
86
87        let response = request_builder.send().await?;
88        self.handle_response(response).await
89    }
90
91    /// Handle the response and parse it into the expected type
92    async fn handle_response<T>(&self, response: Response) -> Result<T, KiteConnectError>
93    where
94        T: DeserializeOwned,
95    {
96        let status = response.status();
97        let response_text = response.text().await?;
98
99        if status.is_success() {
100            // Try to parse as wrapped response first
101            if let Ok(api_response) = serde_json::from_str::<ApiResponse<T>>(&response_text) {
102                Ok(api_response.data)
103            } else if let Ok(data_response) =
104                serde_json::from_str::<DataOnlyResponse<T>>(&response_text)
105            {
106                Ok(data_response.data)
107            } else if let Ok(result) = serde_json::from_str::<T>(&response_text) {
108                Ok(result)
109            } else if let Ok(result) =
110                serde_json::from_value(serde_json::Value::String(response_text.clone()))
111            {
112                // if T = String or similar, return the raw text
113                Ok(result)
114            } else {
115                // failed trying to parse as T. (type mismatch with T and response)
116                let type_name = std::any::type_name::<T>();
117
118                Err(KiteConnectError::new(SerializationError(Error::custom(
119                    format!(
120                        "Failed to parse response as {}. Response (first 500 chars): {}",
121                        type_name,
122                        &response_text.chars().take(500).collect::<String>()
123                    ),
124                ))))
125            }
126        } else {
127            // Parse error response
128            let error: KiteError = serde_json::from_str(&response_text)?;
129            Err(error.into())
130        }
131    }
132
133    /// Get default headers for all requests
134    fn get_default_headers(&self) -> Result<HeaderMap, KiteConnectError> {
135        let mut headers = HeaderMap::new();
136        headers.insert(
137            "X-Kite-Version",
138            HeaderValue::from_static(KITE_HEADER_VERSION),
139        );
140
141        let user_agent = HeaderValue::from_str(&format!(
142            "{}/{}",
143            KITE_CONNECT_RS_NAME, KITE_CONNECT_RS_VERSION
144        ))?;
145        headers.insert("User-Agent", user_agent);
146
147        Ok(headers)
148    }
149
150    pub async fn get<T>(&self, endpoint: &str) -> Result<T, KiteConnectError>
151    where
152        T: DeserializeOwned,
153    {
154        self.do_envelope::<T, ()>(Method::GET, endpoint, None, None, None)
155            .await
156    }
157
158    pub async fn put<T>(&self, endpoint: &str) -> Result<T, KiteConnectError>
159    where
160        T: DeserializeOwned,
161    {
162        self.do_envelope::<T, ()>(Method::PUT, endpoint, None, None, None)
163            .await
164    }
165    pub async fn post<T>(&self, endpoint: &str) -> Result<T, KiteConnectError>
166    where
167        T: DeserializeOwned,
168    {
169        self.do_envelope::<T, ()>(Method::POST, endpoint, None, None, None)
170            .await
171    }
172    pub async fn delete<T>(&self, endpoint: &str) -> Result<T, KiteConnectError>
173    where
174        T: DeserializeOwned,
175    {
176        self.do_envelope::<T, ()>(Method::DELETE, endpoint, None, None, None)
177            .await
178    }
179
180    /// Make a POST request with form parameters
181    pub async fn post_form<T, K: Serialize>(
182        &self,
183        endpoint: &str,
184        params: K,
185    ) -> Result<T, KiteConnectError>
186    where
187        T: DeserializeOwned,
188    {
189        self.do_envelope(
190            Method::POST,
191            endpoint,
192            None,
193            Some(RequestBody::Form(params)),
194            None,
195        )
196        .await
197    }
198
199    /// Make a POST request with JSON body
200    pub async fn post_json<T, K: Serialize>(
201        &self,
202        endpoint: &str,
203        json_body: K,
204    ) -> Result<T, KiteConnectError>
205    where
206        T: DeserializeOwned,
207    {
208        self.do_envelope(
209            Method::POST,
210            endpoint,
211            None,
212            Some(RequestBody::Json(json_body)),
213            None,
214        )
215        .await
216    }
217
218    /// Make a DELETE request with form parameters
219    pub async fn delete_form<T, K: Serialize>(
220        &self,
221        endpoint: &str,
222        params: K,
223    ) -> Result<T, KiteConnectError>
224    where
225        T: DeserializeOwned,
226    {
227        self.do_envelope(
228            Method::DELETE,
229            endpoint,
230            None,
231            Some(RequestBody::Form(params)),
232            None,
233        )
234        .await
235    }
236
237    /// Make a PUT request with form parameters
238    pub async fn put_form<T, K: Serialize>(
239        &self,
240        endpoint: &str,
241        params: K,
242    ) -> Result<T, KiteConnectError>
243    where
244        T: DeserializeOwned,
245    {
246        self.do_envelope(
247            Method::PUT,
248            endpoint,
249            None,
250            Some(RequestBody::Form(params)),
251            None,
252        )
253        .await
254    }
255
256    /// Make a PUT request with JSON body
257    pub async fn put_json<T, K: Serialize>(
258        &self,
259        endpoint: &str,
260        json_body: K,
261    ) -> Result<T, KiteConnectError>
262    where
263        T: DeserializeOwned,
264    {
265        self.do_envelope(
266            Method::PUT,
267            endpoint,
268            None,
269            Some(RequestBody::Json(json_body)),
270            None,
271        )
272        .await
273    }
274
275    /// Make a GET request with query parameters
276    pub async fn get_with_query<T>(
277        &self,
278        endpoint: &str,
279        params: HashMap<String, String>,
280    ) -> Result<T, KiteConnectError>
281    where
282        T: DeserializeOwned,
283    {
284        self.do_envelope::<T, ()>(Method::GET, endpoint, Some(params), None, None)
285            .await
286    }
287
288    /// Make a DELETE request with query parameters
289    pub async fn delete_with_query<T>(
290        &self,
291        endpoint: &str,
292        params: HashMap<String, String>,
293    ) -> Result<T, KiteConnectError>
294    where
295        T: DeserializeOwned,
296    {
297        self.do_envelope::<T, ()>(Method::DELETE, endpoint, Some(params), None, None)
298            .await
299    }
300}