binance_sdk/margin_trading/rest_api/apis/
risk_data_stream_api.rs1#![allow(unused_imports)]
15use async_trait::async_trait;
16use derive_builder::Builder;
17use reqwest;
18use rust_decimal::prelude::*;
19use serde::{Deserialize, Serialize};
20use serde_json::{Value, json};
21use std::collections::BTreeMap;
22
23use crate::common::{
24 config::ConfigurationRestApi,
25 models::{ParamBuildError, RestApiResponse},
26 utils::send_request,
27};
28use crate::margin_trading::rest_api::models;
29
30const HAS_TIME_UNIT: bool = false;
31
32#[async_trait]
33pub trait RiskDataStreamApi: Send + Sync {
34 async fn close_user_data_stream(&self) -> anyhow::Result<RestApiResponse<Value>>;
35 async fn keepalive_user_data_stream(
36 &self,
37 params: KeepaliveUserDataStreamParams,
38 ) -> anyhow::Result<RestApiResponse<Value>>;
39 async fn start_user_data_stream(
40 &self,
41 ) -> anyhow::Result<RestApiResponse<models::StartUserDataStreamResponse>>;
42}
43
44#[derive(Debug, Clone)]
45pub struct RiskDataStreamApiClient {
46 configuration: ConfigurationRestApi,
47}
48
49impl RiskDataStreamApiClient {
50 pub fn new(configuration: ConfigurationRestApi) -> Self {
51 Self { configuration }
52 }
53}
54
55#[derive(Clone, Debug, Builder)]
60#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
61pub struct KeepaliveUserDataStreamParams {
62 #[builder(setter(into))]
67 pub listen_key: String,
68}
69
70impl KeepaliveUserDataStreamParams {
71 #[must_use]
78 pub fn builder(listen_key: String) -> KeepaliveUserDataStreamParamsBuilder {
79 KeepaliveUserDataStreamParamsBuilder::default().listen_key(listen_key)
80 }
81}
82
83#[async_trait]
84impl RiskDataStreamApi for RiskDataStreamApiClient {
85 async fn close_user_data_stream(&self) -> anyhow::Result<RestApiResponse<Value>> {
86 let query_params = BTreeMap::new();
87 let body_params = BTreeMap::new();
88
89 send_request::<Value>(
90 &self.configuration,
91 "/sapi/v1/margin/listen-key",
92 reqwest::Method::DELETE,
93 query_params,
94 body_params,
95 if HAS_TIME_UNIT {
96 self.configuration.time_unit
97 } else {
98 None
99 },
100 false,
101 )
102 .await
103 }
104
105 async fn keepalive_user_data_stream(
106 &self,
107 params: KeepaliveUserDataStreamParams,
108 ) -> anyhow::Result<RestApiResponse<Value>> {
109 let KeepaliveUserDataStreamParams { listen_key } = params;
110
111 let mut query_params = BTreeMap::new();
112 let body_params = BTreeMap::new();
113
114 query_params.insert("listenKey".to_string(), json!(listen_key));
115
116 send_request::<Value>(
117 &self.configuration,
118 "/sapi/v1/margin/listen-key",
119 reqwest::Method::PUT,
120 query_params,
121 body_params,
122 if HAS_TIME_UNIT {
123 self.configuration.time_unit
124 } else {
125 None
126 },
127 false,
128 )
129 .await
130 }
131
132 async fn start_user_data_stream(
133 &self,
134 ) -> anyhow::Result<RestApiResponse<models::StartUserDataStreamResponse>> {
135 let query_params = BTreeMap::new();
136 let body_params = BTreeMap::new();
137
138 send_request::<models::StartUserDataStreamResponse>(
139 &self.configuration,
140 "/sapi/v1/margin/listen-key",
141 reqwest::Method::POST,
142 query_params,
143 body_params,
144 if HAS_TIME_UNIT {
145 self.configuration.time_unit
146 } else {
147 None
148 },
149 false,
150 )
151 .await
152 }
153}
154
155#[cfg(all(test, feature = "margin_trading"))]
156mod tests {
157 use super::*;
158 use crate::TOKIO_SHARED_RT;
159 use crate::{errors::ConnectorError, models::DataFuture, models::RestApiRateLimit};
160 use async_trait::async_trait;
161 use std::collections::HashMap;
162
163 struct DummyRestApiResponse<T> {
164 inner: Box<dyn FnOnce() -> DataFuture<Result<T, ConnectorError>> + Send + Sync>,
165 status: u16,
166 headers: HashMap<String, String>,
167 rate_limits: Option<Vec<RestApiRateLimit>>,
168 }
169
170 impl<T> From<DummyRestApiResponse<T>> for RestApiResponse<T> {
171 fn from(dummy: DummyRestApiResponse<T>) -> Self {
172 Self {
173 data_fn: dummy.inner,
174 status: dummy.status,
175 headers: dummy.headers,
176 rate_limits: dummy.rate_limits,
177 }
178 }
179 }
180
181 struct MockRiskDataStreamApiClient {
182 force_error: bool,
183 }
184
185 #[async_trait]
186 impl RiskDataStreamApi for MockRiskDataStreamApiClient {
187 async fn close_user_data_stream(&self) -> anyhow::Result<RestApiResponse<Value>> {
188 if self.force_error {
189 return Err(ConnectorError::ConnectorClientError {
190 msg: "ResponseError".to_string(),
191 code: None,
192 }
193 .into());
194 }
195
196 let dummy_response = Value::Null;
197
198 let dummy = DummyRestApiResponse {
199 inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
200 status: 200,
201 headers: HashMap::new(),
202 rate_limits: None,
203 };
204
205 Ok(dummy.into())
206 }
207
208 async fn keepalive_user_data_stream(
209 &self,
210 _params: KeepaliveUserDataStreamParams,
211 ) -> anyhow::Result<RestApiResponse<Value>> {
212 if self.force_error {
213 return Err(ConnectorError::ConnectorClientError {
214 msg: "ResponseError".to_string(),
215 code: None,
216 }
217 .into());
218 }
219
220 let dummy_response = Value::Null;
221
222 let dummy = DummyRestApiResponse {
223 inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
224 status: 200,
225 headers: HashMap::new(),
226 rate_limits: None,
227 };
228
229 Ok(dummy.into())
230 }
231
232 async fn start_user_data_stream(
233 &self,
234 ) -> anyhow::Result<RestApiResponse<models::StartUserDataStreamResponse>> {
235 if self.force_error {
236 return Err(ConnectorError::ConnectorClientError {
237 msg: "ResponseError".to_string(),
238 code: None,
239 }
240 .into());
241 }
242
243 let resp_json: Value = serde_json::from_str(
244 r#"{"listenKey":"T3ee22BIYuWqmvne0HNq2A2WsFlEtLhvWCtItw6ffhhd"}"#,
245 )
246 .unwrap();
247 let dummy_response: models::StartUserDataStreamResponse =
248 serde_json::from_value(resp_json.clone())
249 .expect("should parse into models::StartUserDataStreamResponse");
250
251 let dummy = DummyRestApiResponse {
252 inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
253 status: 200,
254 headers: HashMap::new(),
255 rate_limits: None,
256 };
257
258 Ok(dummy.into())
259 }
260 }
261
262 #[test]
263 fn close_user_data_stream_required_params_success() {
264 TOKIO_SHARED_RT.block_on(async {
265 let client = MockRiskDataStreamApiClient { force_error: false };
266
267 let expected_response = Value::Null;
268
269 let resp = client
270 .close_user_data_stream()
271 .await
272 .expect("Expected a response");
273 let data_future = resp.data();
274 let actual_response = data_future.await.unwrap();
275 assert_eq!(actual_response, expected_response);
276 });
277 }
278
279 #[test]
280 fn close_user_data_stream_optional_params_success() {
281 TOKIO_SHARED_RT.block_on(async {
282 let client = MockRiskDataStreamApiClient { force_error: false };
283
284 let expected_response = Value::Null;
285
286 let resp = client
287 .close_user_data_stream()
288 .await
289 .expect("Expected a response");
290 let data_future = resp.data();
291 let actual_response = data_future.await.unwrap();
292 assert_eq!(actual_response, expected_response);
293 });
294 }
295
296 #[test]
297 fn close_user_data_stream_response_error() {
298 TOKIO_SHARED_RT.block_on(async {
299 let client = MockRiskDataStreamApiClient { force_error: true };
300
301 match client.close_user_data_stream().await {
302 Ok(_) => panic!("Expected an error"),
303 Err(err) => {
304 assert_eq!(err.to_string(), "Connector client error: ResponseError");
305 }
306 }
307 });
308 }
309
310 #[test]
311 fn keepalive_user_data_stream_required_params_success() {
312 TOKIO_SHARED_RT.block_on(async {
313 let client = MockRiskDataStreamApiClient { force_error: false };
314
315 let params = KeepaliveUserDataStreamParams::builder("listen_key_example".to_string())
316 .build()
317 .unwrap();
318
319 let expected_response = Value::Null;
320
321 let resp = client
322 .keepalive_user_data_stream(params)
323 .await
324 .expect("Expected a response");
325 let data_future = resp.data();
326 let actual_response = data_future.await.unwrap();
327 assert_eq!(actual_response, expected_response);
328 });
329 }
330
331 #[test]
332 fn keepalive_user_data_stream_optional_params_success() {
333 TOKIO_SHARED_RT.block_on(async {
334 let client = MockRiskDataStreamApiClient { force_error: false };
335
336 let params = KeepaliveUserDataStreamParams::builder("listen_key_example".to_string())
337 .build()
338 .unwrap();
339
340 let expected_response = Value::Null;
341
342 let resp = client
343 .keepalive_user_data_stream(params)
344 .await
345 .expect("Expected a response");
346 let data_future = resp.data();
347 let actual_response = data_future.await.unwrap();
348 assert_eq!(actual_response, expected_response);
349 });
350 }
351
352 #[test]
353 fn keepalive_user_data_stream_response_error() {
354 TOKIO_SHARED_RT.block_on(async {
355 let client = MockRiskDataStreamApiClient { force_error: true };
356
357 let params = KeepaliveUserDataStreamParams::builder("listen_key_example".to_string())
358 .build()
359 .unwrap();
360
361 match client.keepalive_user_data_stream(params).await {
362 Ok(_) => panic!("Expected an error"),
363 Err(err) => {
364 assert_eq!(err.to_string(), "Connector client error: ResponseError");
365 }
366 }
367 });
368 }
369
370 #[test]
371 fn start_user_data_stream_required_params_success() {
372 TOKIO_SHARED_RT.block_on(async {
373 let client = MockRiskDataStreamApiClient { force_error: false };
374
375 let resp_json: Value = serde_json::from_str(
376 r#"{"listenKey":"T3ee22BIYuWqmvne0HNq2A2WsFlEtLhvWCtItw6ffhhd"}"#,
377 )
378 .unwrap();
379 let expected_response: models::StartUserDataStreamResponse =
380 serde_json::from_value(resp_json.clone())
381 .expect("should parse into models::StartUserDataStreamResponse");
382
383 let resp = client
384 .start_user_data_stream()
385 .await
386 .expect("Expected a response");
387 let data_future = resp.data();
388 let actual_response = data_future.await.unwrap();
389 assert_eq!(actual_response, expected_response);
390 });
391 }
392
393 #[test]
394 fn start_user_data_stream_optional_params_success() {
395 TOKIO_SHARED_RT.block_on(async {
396 let client = MockRiskDataStreamApiClient { force_error: false };
397
398 let resp_json: Value = serde_json::from_str(
399 r#"{"listenKey":"T3ee22BIYuWqmvne0HNq2A2WsFlEtLhvWCtItw6ffhhd"}"#,
400 )
401 .unwrap();
402 let expected_response: models::StartUserDataStreamResponse =
403 serde_json::from_value(resp_json.clone())
404 .expect("should parse into models::StartUserDataStreamResponse");
405
406 let resp = client
407 .start_user_data_stream()
408 .await
409 .expect("Expected a response");
410 let data_future = resp.data();
411 let actual_response = data_future.await.unwrap();
412 assert_eq!(actual_response, expected_response);
413 });
414 }
415
416 #[test]
417 fn start_user_data_stream_response_error() {
418 TOKIO_SHARED_RT.block_on(async {
419 let client = MockRiskDataStreamApiClient { force_error: true };
420
421 match client.start_user_data_stream().await {
422 Ok(_) => panic!("Expected an error"),
423 Err(err) => {
424 assert_eq!(err.to_string(), "Connector client error: ResponseError");
425 }
426 }
427 });
428 }
429}