1#![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::wallet::rest_api::models;
29
30const HAS_TIME_UNIT: bool = false;
31
32#[async_trait]
33pub trait AccountApi: Send + Sync {
34 async fn account_api_trading_status(
35 &self,
36 params: AccountApiTradingStatusParams,
37 ) -> anyhow::Result<RestApiResponse<models::AccountApiTradingStatusResponse>>;
38 async fn account_info(
39 &self,
40 params: AccountInfoParams,
41 ) -> anyhow::Result<RestApiResponse<models::AccountInfoResponse>>;
42 async fn account_status(
43 &self,
44 params: AccountStatusParams,
45 ) -> anyhow::Result<RestApiResponse<models::AccountStatusResponse>>;
46 async fn daily_account_snapshot(
47 &self,
48 params: DailyAccountSnapshotParams,
49 ) -> anyhow::Result<RestApiResponse<models::DailyAccountSnapshotResponse>>;
50 async fn disable_fast_withdraw_switch(
51 &self,
52 params: DisableFastWithdrawSwitchParams,
53 ) -> anyhow::Result<RestApiResponse<Value>>;
54 async fn enable_fast_withdraw_switch(
55 &self,
56 params: EnableFastWithdrawSwitchParams,
57 ) -> anyhow::Result<RestApiResponse<Value>>;
58 async fn get_api_key_permission(
59 &self,
60 params: GetApiKeyPermissionParams,
61 ) -> anyhow::Result<RestApiResponse<models::GetApiKeyPermissionResponse>>;
62}
63
64#[derive(Debug, Clone)]
65pub struct AccountApiClient {
66 configuration: ConfigurationRestApi,
67}
68
69impl AccountApiClient {
70 pub fn new(configuration: ConfigurationRestApi) -> Self {
71 Self { configuration }
72 }
73}
74
75#[allow(non_camel_case_types)]
76#[derive(Debug, Clone, Serialize, Deserialize)]
77pub enum DailyAccountSnapshotTypeEnum {
78 #[serde(rename = "SPOT")]
79 Spot,
80 #[serde(rename = "MARGIN")]
81 Margin,
82 #[serde(rename = "FUTURES")]
83 Futures,
84}
85
86impl DailyAccountSnapshotTypeEnum {
87 #[must_use]
88 pub fn as_str(&self) -> &'static str {
89 match self {
90 Self::Spot => "SPOT",
91 Self::Margin => "MARGIN",
92 Self::Futures => "FUTURES",
93 }
94 }
95}
96
97impl std::str::FromStr for DailyAccountSnapshotTypeEnum {
98 type Err = Box<dyn std::error::Error + Send + Sync>;
99
100 fn from_str(s: &str) -> Result<Self, Self::Err> {
101 match s {
102 "SPOT" => Ok(Self::Spot),
103 "MARGIN" => Ok(Self::Margin),
104 "FUTURES" => Ok(Self::Futures),
105 other => Err(format!("invalid DailyAccountSnapshotTypeEnum: {}", other).into()),
106 }
107 }
108}
109
110#[derive(Clone, Debug, Builder, Deserialize, Default)]
115#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
116pub struct AccountApiTradingStatusParams {
117 #[builder(setter(into), default)]
122 #[serde(rename = "recvWindow", default)]
123 pub recv_window: Option<i64>,
124}
125
126impl AccountApiTradingStatusParams {
127 #[must_use]
130 pub fn builder() -> AccountApiTradingStatusParamsBuilder {
131 AccountApiTradingStatusParamsBuilder::default()
132 }
133}
134#[derive(Clone, Debug, Builder, Deserialize, Default)]
139#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
140pub struct AccountInfoParams {
141 #[builder(setter(into), default)]
146 #[serde(rename = "recvWindow", default)]
147 pub recv_window: Option<i64>,
148}
149
150impl AccountInfoParams {
151 #[must_use]
154 pub fn builder() -> AccountInfoParamsBuilder {
155 AccountInfoParamsBuilder::default()
156 }
157}
158#[derive(Clone, Debug, Builder, Deserialize, Default)]
163#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
164pub struct AccountStatusParams {
165 #[builder(setter(into), default)]
170 #[serde(rename = "recvWindow", default)]
171 pub recv_window: Option<i64>,
172}
173
174impl AccountStatusParams {
175 #[must_use]
178 pub fn builder() -> AccountStatusParamsBuilder {
179 AccountStatusParamsBuilder::default()
180 }
181}
182#[derive(Clone, Debug, Builder, Deserialize)]
187#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
188pub struct DailyAccountSnapshotParams {
189 #[builder(setter(into))]
194 #[serde(rename = "type")]
195 pub r#type: DailyAccountSnapshotTypeEnum,
196 #[builder(setter(into), default)]
201 #[serde(rename = "startTime", default)]
202 pub start_time: Option<i64>,
203 #[builder(setter(into), default)]
208 #[serde(rename = "endTime", default)]
209 pub end_time: Option<i64>,
210 #[builder(setter(into), default)]
215 #[serde(rename = "limit", default)]
216 pub limit: Option<i64>,
217 #[builder(setter(into), default)]
222 #[serde(rename = "recvWindow", default)]
223 pub recv_window: Option<i64>,
224}
225
226impl DailyAccountSnapshotParams {
227 #[must_use]
234 pub fn builder(r#type: DailyAccountSnapshotTypeEnum) -> DailyAccountSnapshotParamsBuilder {
235 DailyAccountSnapshotParamsBuilder::default().r#type(r#type)
236 }
237}
238#[derive(Clone, Debug, Builder, Deserialize, Default)]
243#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
244pub struct DisableFastWithdrawSwitchParams {
245 #[builder(setter(into), default)]
250 #[serde(rename = "recvWindow", default)]
251 pub recv_window: Option<i64>,
252}
253
254impl DisableFastWithdrawSwitchParams {
255 #[must_use]
258 pub fn builder() -> DisableFastWithdrawSwitchParamsBuilder {
259 DisableFastWithdrawSwitchParamsBuilder::default()
260 }
261}
262#[derive(Clone, Debug, Builder, Deserialize, Default)]
267#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
268pub struct EnableFastWithdrawSwitchParams {
269 #[builder(setter(into), default)]
274 #[serde(rename = "recvWindow", default)]
275 pub recv_window: Option<i64>,
276}
277
278impl EnableFastWithdrawSwitchParams {
279 #[must_use]
282 pub fn builder() -> EnableFastWithdrawSwitchParamsBuilder {
283 EnableFastWithdrawSwitchParamsBuilder::default()
284 }
285}
286#[derive(Clone, Debug, Builder, Deserialize, Default)]
291#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
292pub struct GetApiKeyPermissionParams {
293 #[builder(setter(into), default)]
298 #[serde(rename = "recvWindow", default)]
299 pub recv_window: Option<i64>,
300}
301
302impl GetApiKeyPermissionParams {
303 #[must_use]
306 pub fn builder() -> GetApiKeyPermissionParamsBuilder {
307 GetApiKeyPermissionParamsBuilder::default()
308 }
309}
310
311#[async_trait]
312impl AccountApi for AccountApiClient {
313 async fn account_api_trading_status(
314 &self,
315 params: AccountApiTradingStatusParams,
316 ) -> anyhow::Result<RestApiResponse<models::AccountApiTradingStatusResponse>> {
317 let AccountApiTradingStatusParams { recv_window } = params;
318
319 let mut query_params = BTreeMap::new();
320 let body_params = BTreeMap::new();
321
322 if let Some(rw) = recv_window {
323 query_params.insert("recvWindow".to_string(), json!(rw));
324 }
325
326 send_request::<models::AccountApiTradingStatusResponse>(
327 &self.configuration,
328 "/sapi/v1/account/apiTradingStatus",
329 reqwest::Method::GET,
330 query_params,
331 body_params,
332 if HAS_TIME_UNIT {
333 self.configuration.time_unit
334 } else {
335 None
336 },
337 true,
338 )
339 .await
340 }
341
342 async fn account_info(
343 &self,
344 params: AccountInfoParams,
345 ) -> anyhow::Result<RestApiResponse<models::AccountInfoResponse>> {
346 let AccountInfoParams { recv_window } = params;
347
348 let mut query_params = BTreeMap::new();
349 let body_params = BTreeMap::new();
350
351 if let Some(rw) = recv_window {
352 query_params.insert("recvWindow".to_string(), json!(rw));
353 }
354
355 send_request::<models::AccountInfoResponse>(
356 &self.configuration,
357 "/sapi/v1/account/info",
358 reqwest::Method::GET,
359 query_params,
360 body_params,
361 if HAS_TIME_UNIT {
362 self.configuration.time_unit
363 } else {
364 None
365 },
366 true,
367 )
368 .await
369 }
370
371 async fn account_status(
372 &self,
373 params: AccountStatusParams,
374 ) -> anyhow::Result<RestApiResponse<models::AccountStatusResponse>> {
375 let AccountStatusParams { recv_window } = params;
376
377 let mut query_params = BTreeMap::new();
378 let body_params = BTreeMap::new();
379
380 if let Some(rw) = recv_window {
381 query_params.insert("recvWindow".to_string(), json!(rw));
382 }
383
384 send_request::<models::AccountStatusResponse>(
385 &self.configuration,
386 "/sapi/v1/account/status",
387 reqwest::Method::GET,
388 query_params,
389 body_params,
390 if HAS_TIME_UNIT {
391 self.configuration.time_unit
392 } else {
393 None
394 },
395 true,
396 )
397 .await
398 }
399
400 async fn daily_account_snapshot(
401 &self,
402 params: DailyAccountSnapshotParams,
403 ) -> anyhow::Result<RestApiResponse<models::DailyAccountSnapshotResponse>> {
404 let DailyAccountSnapshotParams {
405 r#type,
406 start_time,
407 end_time,
408 limit,
409 recv_window,
410 } = params;
411
412 let mut query_params = BTreeMap::new();
413 let body_params = BTreeMap::new();
414
415 query_params.insert("type".to_string(), json!(r#type));
416
417 if let Some(rw) = start_time {
418 query_params.insert("startTime".to_string(), json!(rw));
419 }
420
421 if let Some(rw) = end_time {
422 query_params.insert("endTime".to_string(), json!(rw));
423 }
424
425 if let Some(rw) = limit {
426 query_params.insert("limit".to_string(), json!(rw));
427 }
428
429 if let Some(rw) = recv_window {
430 query_params.insert("recvWindow".to_string(), json!(rw));
431 }
432
433 send_request::<models::DailyAccountSnapshotResponse>(
434 &self.configuration,
435 "/sapi/v1/accountSnapshot",
436 reqwest::Method::GET,
437 query_params,
438 body_params,
439 if HAS_TIME_UNIT {
440 self.configuration.time_unit
441 } else {
442 None
443 },
444 true,
445 )
446 .await
447 }
448
449 async fn disable_fast_withdraw_switch(
450 &self,
451 params: DisableFastWithdrawSwitchParams,
452 ) -> anyhow::Result<RestApiResponse<Value>> {
453 let DisableFastWithdrawSwitchParams { recv_window } = params;
454
455 let mut query_params = BTreeMap::new();
456 let body_params = BTreeMap::new();
457
458 if let Some(rw) = recv_window {
459 query_params.insert("recvWindow".to_string(), json!(rw));
460 }
461
462 send_request::<Value>(
463 &self.configuration,
464 "/sapi/v1/account/disableFastWithdrawSwitch",
465 reqwest::Method::POST,
466 query_params,
467 body_params,
468 if HAS_TIME_UNIT {
469 self.configuration.time_unit
470 } else {
471 None
472 },
473 true,
474 )
475 .await
476 }
477
478 async fn enable_fast_withdraw_switch(
479 &self,
480 params: EnableFastWithdrawSwitchParams,
481 ) -> anyhow::Result<RestApiResponse<Value>> {
482 let EnableFastWithdrawSwitchParams { recv_window } = params;
483
484 let mut query_params = BTreeMap::new();
485 let body_params = BTreeMap::new();
486
487 if let Some(rw) = recv_window {
488 query_params.insert("recvWindow".to_string(), json!(rw));
489 }
490
491 send_request::<Value>(
492 &self.configuration,
493 "/sapi/v1/account/enableFastWithdrawSwitch",
494 reqwest::Method::POST,
495 query_params,
496 body_params,
497 if HAS_TIME_UNIT {
498 self.configuration.time_unit
499 } else {
500 None
501 },
502 true,
503 )
504 .await
505 }
506
507 async fn get_api_key_permission(
508 &self,
509 params: GetApiKeyPermissionParams,
510 ) -> anyhow::Result<RestApiResponse<models::GetApiKeyPermissionResponse>> {
511 let GetApiKeyPermissionParams { recv_window } = params;
512
513 let mut query_params = BTreeMap::new();
514 let body_params = BTreeMap::new();
515
516 if let Some(rw) = recv_window {
517 query_params.insert("recvWindow".to_string(), json!(rw));
518 }
519
520 send_request::<models::GetApiKeyPermissionResponse>(
521 &self.configuration,
522 "/sapi/v1/account/apiRestrictions",
523 reqwest::Method::GET,
524 query_params,
525 body_params,
526 if HAS_TIME_UNIT {
527 self.configuration.time_unit
528 } else {
529 None
530 },
531 true,
532 )
533 .await
534 }
535}
536
537#[cfg(all(test, feature = "wallet"))]
538mod tests {
539 use super::*;
540 use crate::TOKIO_SHARED_RT;
541 use crate::{errors::ConnectorError, models::DataFuture, models::RestApiRateLimit};
542 use async_trait::async_trait;
543 use std::collections::HashMap;
544
545 struct DummyRestApiResponse<T> {
546 inner: Box<dyn FnOnce() -> DataFuture<Result<T, ConnectorError>> + Send + Sync>,
547 status: u16,
548 headers: HashMap<String, String>,
549 rate_limits: Option<Vec<RestApiRateLimit>>,
550 }
551
552 impl<T> From<DummyRestApiResponse<T>> for RestApiResponse<T> {
553 fn from(dummy: DummyRestApiResponse<T>) -> Self {
554 Self {
555 data_fn: dummy.inner,
556 status: dummy.status,
557 headers: dummy.headers,
558 rate_limits: dummy.rate_limits,
559 }
560 }
561 }
562
563 struct MockAccountApiClient {
564 force_error: bool,
565 }
566
567 #[async_trait]
568 impl AccountApi for MockAccountApiClient {
569 async fn account_api_trading_status(
570 &self,
571 _params: AccountApiTradingStatusParams,
572 ) -> anyhow::Result<RestApiResponse<models::AccountApiTradingStatusResponse>> {
573 if self.force_error {
574 return Err(ConnectorError::ConnectorClientError {
575 msg: "ResponseError".to_string(),
576 code: None,
577 }
578 .into());
579 }
580
581 let resp_json: Value = serde_json::from_str(r#"{"data":{"isLocked":false,"plannedRecoverTime":0,"triggerCondition":{"GCR":150,"IFER":150,"UFR":300},"updateTime":1547630471725}}"#).unwrap_or_else(|_| serde_json::json!({}));
582 let dummy_response: models::AccountApiTradingStatusResponse =
583 serde_json::from_value(resp_json.clone())
584 .expect("should parse into models::AccountApiTradingStatusResponse");
585
586 let dummy = DummyRestApiResponse {
587 inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
588 status: 200,
589 headers: HashMap::new(),
590 rate_limits: None,
591 };
592
593 Ok(dummy.into())
594 }
595
596 async fn account_info(
597 &self,
598 _params: AccountInfoParams,
599 ) -> anyhow::Result<RestApiResponse<models::AccountInfoResponse>> {
600 if self.force_error {
601 return Err(ConnectorError::ConnectorClientError {
602 msg: "ResponseError".to_string(),
603 code: None,
604 }
605 .into());
606 }
607
608 let resp_json: Value = serde_json::from_str(r#"{"vipLevel":0,"isMarginEnabled":true,"isFutureEnabled":true,"isOptionsEnabled":true,"isPortfolioMarginRetailEnabled":true}"#).unwrap_or_else(|_| serde_json::json!({}));
609 let dummy_response: models::AccountInfoResponse =
610 serde_json::from_value(resp_json.clone())
611 .expect("should parse into models::AccountInfoResponse");
612
613 let dummy = DummyRestApiResponse {
614 inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
615 status: 200,
616 headers: HashMap::new(),
617 rate_limits: None,
618 };
619
620 Ok(dummy.into())
621 }
622
623 async fn account_status(
624 &self,
625 _params: AccountStatusParams,
626 ) -> anyhow::Result<RestApiResponse<models::AccountStatusResponse>> {
627 if self.force_error {
628 return Err(ConnectorError::ConnectorClientError {
629 msg: "ResponseError".to_string(),
630 code: None,
631 }
632 .into());
633 }
634
635 let resp_json: Value = serde_json::from_str(r#"{"data":"Normal"}"#)
636 .unwrap_or_else(|_| serde_json::json!({}));
637 let dummy_response: models::AccountStatusResponse =
638 serde_json::from_value(resp_json.clone())
639 .expect("should parse into models::AccountStatusResponse");
640
641 let dummy = DummyRestApiResponse {
642 inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
643 status: 200,
644 headers: HashMap::new(),
645 rate_limits: None,
646 };
647
648 Ok(dummy.into())
649 }
650
651 async fn daily_account_snapshot(
652 &self,
653 _params: DailyAccountSnapshotParams,
654 ) -> anyhow::Result<RestApiResponse<models::DailyAccountSnapshotResponse>> {
655 if self.force_error {
656 return Err(ConnectorError::ConnectorClientError {
657 msg: "ResponseError".to_string(),
658 code: None,
659 }
660 .into());
661 }
662
663 let resp_json: Value = serde_json::from_str(r#"{"code":200,"msg":"","snapshotVos":[{"data":{"balances":[{"asset":"BTC","free":"0.09905021","locked":"0.00000000"}],"totalAssetOfBtc":"0.09942700","marginLevel":"2748.02909813","totalLiabilityOfBtc":"0.00000100","totalNetAssetOfBtc":"0.00274750","userAssets":[{"asset":"XRP","borrowed":"0.00000000","free":"1.00000000","interest":"0.00000000","locked":"0.00000000","netAsset":"1.00000000"}],"assets":[{"asset":"USDT","marginBalance":"118.99782335","walletBalance":"120.23811389"}],"position":[{"entryPrice":"7130.41000000","markPrice":"7257.66239673","positionAmt":"0.01000000","symbol":"BTCUSDT","unRealizedProfit":"1.24029054"}]},"type":"spot","updateTime":1576281599000}]}"#).unwrap_or_else(|_| serde_json::json!({}));
664 let dummy_response: models::DailyAccountSnapshotResponse =
665 serde_json::from_value(resp_json.clone())
666 .expect("should parse into models::DailyAccountSnapshotResponse");
667
668 let dummy = DummyRestApiResponse {
669 inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
670 status: 200,
671 headers: HashMap::new(),
672 rate_limits: None,
673 };
674
675 Ok(dummy.into())
676 }
677
678 async fn disable_fast_withdraw_switch(
679 &self,
680 _params: DisableFastWithdrawSwitchParams,
681 ) -> anyhow::Result<RestApiResponse<Value>> {
682 if self.force_error {
683 return Err(ConnectorError::ConnectorClientError {
684 msg: "ResponseError".to_string(),
685 code: None,
686 }
687 .into());
688 }
689
690 let dummy_response = Value::Null;
691
692 let dummy = DummyRestApiResponse {
693 inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
694 status: 200,
695 headers: HashMap::new(),
696 rate_limits: None,
697 };
698
699 Ok(dummy.into())
700 }
701
702 async fn enable_fast_withdraw_switch(
703 &self,
704 _params: EnableFastWithdrawSwitchParams,
705 ) -> anyhow::Result<RestApiResponse<Value>> {
706 if self.force_error {
707 return Err(ConnectorError::ConnectorClientError {
708 msg: "ResponseError".to_string(),
709 code: None,
710 }
711 .into());
712 }
713
714 let dummy_response = Value::Null;
715
716 let dummy = DummyRestApiResponse {
717 inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
718 status: 200,
719 headers: HashMap::new(),
720 rate_limits: None,
721 };
722
723 Ok(dummy.into())
724 }
725
726 async fn get_api_key_permission(
727 &self,
728 _params: GetApiKeyPermissionParams,
729 ) -> anyhow::Result<RestApiResponse<models::GetApiKeyPermissionResponse>> {
730 if self.force_error {
731 return Err(ConnectorError::ConnectorClientError {
732 msg: "ResponseError".to_string(),
733 code: None,
734 }
735 .into());
736 }
737
738 let resp_json: Value = serde_json::from_str(r#"{"ipRestrict":false,"createTime":1623840271000,"enableReading":true,"enableWithdrawals":false,"enableInternalTransfer":true,"enableMargin":false,"enableFutures":false,"permitsUniversalTransfer":true,"enableVanillaOptions":false,"enableFixApiTrade":false,"enableFixReadOnly":true,"enableSpotAndMarginTrading":false,"enablePortfolioMarginTrading":true}"#).unwrap_or_else(|_| serde_json::json!({}));
739 let dummy_response: models::GetApiKeyPermissionResponse =
740 serde_json::from_value(resp_json.clone())
741 .expect("should parse into models::GetApiKeyPermissionResponse");
742
743 let dummy = DummyRestApiResponse {
744 inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
745 status: 200,
746 headers: HashMap::new(),
747 rate_limits: None,
748 };
749
750 Ok(dummy.into())
751 }
752 }
753
754 #[test]
755 fn account_api_trading_status_required_params_success() {
756 TOKIO_SHARED_RT.block_on(async {
757 let client = MockAccountApiClient { force_error: false };
758
759 let params = AccountApiTradingStatusParams::builder().build().unwrap();
760
761 let resp_json: Value = serde_json::from_str(r#"{"data":{"isLocked":false,"plannedRecoverTime":0,"triggerCondition":{"GCR":150,"IFER":150,"UFR":300},"updateTime":1547630471725}}"#).unwrap_or_else(|_| serde_json::json!({}));
762 let expected_response : models::AccountApiTradingStatusResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::AccountApiTradingStatusResponse");
763
764 let resp = client.account_api_trading_status(params).await.expect("Expected a response");
765 let data_future = resp.data();
766 let actual_response = data_future.await.unwrap();
767 assert_eq!(actual_response, expected_response);
768 });
769 }
770
771 #[test]
772 fn account_api_trading_status_optional_params_success() {
773 TOKIO_SHARED_RT.block_on(async {
774 let client = MockAccountApiClient { force_error: false };
775
776 let params = AccountApiTradingStatusParams::builder().recv_window(5000).build().unwrap();
777
778 let resp_json: Value = serde_json::from_str(r#"{"data":{"isLocked":false,"plannedRecoverTime":0,"triggerCondition":{"GCR":150,"IFER":150,"UFR":300},"updateTime":1547630471725}}"#).unwrap_or_else(|_| serde_json::json!({}));
779 let expected_response : models::AccountApiTradingStatusResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::AccountApiTradingStatusResponse");
780
781 let resp = client.account_api_trading_status(params).await.expect("Expected a response");
782 let data_future = resp.data();
783 let actual_response = data_future.await.unwrap();
784 assert_eq!(actual_response, expected_response);
785 });
786 }
787
788 #[test]
789 fn account_api_trading_status_response_error() {
790 TOKIO_SHARED_RT.block_on(async {
791 let client = MockAccountApiClient { force_error: true };
792
793 let params = AccountApiTradingStatusParams::builder().build().unwrap();
794
795 match client.account_api_trading_status(params).await {
796 Ok(_) => panic!("Expected an error"),
797 Err(err) => {
798 assert_eq!(err.to_string(), "Connector client error: ResponseError");
799 }
800 }
801 });
802 }
803
804 #[test]
805 fn account_info_required_params_success() {
806 TOKIO_SHARED_RT.block_on(async {
807 let client = MockAccountApiClient { force_error: false };
808
809 let params = AccountInfoParams::builder().build().unwrap();
810
811 let resp_json: Value = serde_json::from_str(r#"{"vipLevel":0,"isMarginEnabled":true,"isFutureEnabled":true,"isOptionsEnabled":true,"isPortfolioMarginRetailEnabled":true}"#).unwrap_or_else(|_| serde_json::json!({}));
812 let expected_response : models::AccountInfoResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::AccountInfoResponse");
813
814 let resp = client.account_info(params).await.expect("Expected a response");
815 let data_future = resp.data();
816 let actual_response = data_future.await.unwrap();
817 assert_eq!(actual_response, expected_response);
818 });
819 }
820
821 #[test]
822 fn account_info_optional_params_success() {
823 TOKIO_SHARED_RT.block_on(async {
824 let client = MockAccountApiClient { force_error: false };
825
826 let params = AccountInfoParams::builder().recv_window(5000).build().unwrap();
827
828 let resp_json: Value = serde_json::from_str(r#"{"vipLevel":0,"isMarginEnabled":true,"isFutureEnabled":true,"isOptionsEnabled":true,"isPortfolioMarginRetailEnabled":true}"#).unwrap_or_else(|_| serde_json::json!({}));
829 let expected_response : models::AccountInfoResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::AccountInfoResponse");
830
831 let resp = client.account_info(params).await.expect("Expected a response");
832 let data_future = resp.data();
833 let actual_response = data_future.await.unwrap();
834 assert_eq!(actual_response, expected_response);
835 });
836 }
837
838 #[test]
839 fn account_info_response_error() {
840 TOKIO_SHARED_RT.block_on(async {
841 let client = MockAccountApiClient { force_error: true };
842
843 let params = AccountInfoParams::builder().build().unwrap();
844
845 match client.account_info(params).await {
846 Ok(_) => panic!("Expected an error"),
847 Err(err) => {
848 assert_eq!(err.to_string(), "Connector client error: ResponseError");
849 }
850 }
851 });
852 }
853
854 #[test]
855 fn account_status_required_params_success() {
856 TOKIO_SHARED_RT.block_on(async {
857 let client = MockAccountApiClient { force_error: false };
858
859 let params = AccountStatusParams::builder().build().unwrap();
860
861 let resp_json: Value = serde_json::from_str(r#"{"data":"Normal"}"#)
862 .unwrap_or_else(|_| serde_json::json!({}));
863 let expected_response: models::AccountStatusResponse =
864 serde_json::from_value(resp_json.clone())
865 .expect("should parse into models::AccountStatusResponse");
866
867 let resp = client
868 .account_status(params)
869 .await
870 .expect("Expected a response");
871 let data_future = resp.data();
872 let actual_response = data_future.await.unwrap();
873 assert_eq!(actual_response, expected_response);
874 });
875 }
876
877 #[test]
878 fn account_status_optional_params_success() {
879 TOKIO_SHARED_RT.block_on(async {
880 let client = MockAccountApiClient { force_error: false };
881
882 let params = AccountStatusParams::builder()
883 .recv_window(5000)
884 .build()
885 .unwrap();
886
887 let resp_json: Value = serde_json::from_str(r#"{"data":"Normal"}"#)
888 .unwrap_or_else(|_| serde_json::json!({}));
889 let expected_response: models::AccountStatusResponse =
890 serde_json::from_value(resp_json.clone())
891 .expect("should parse into models::AccountStatusResponse");
892
893 let resp = client
894 .account_status(params)
895 .await
896 .expect("Expected a response");
897 let data_future = resp.data();
898 let actual_response = data_future.await.unwrap();
899 assert_eq!(actual_response, expected_response);
900 });
901 }
902
903 #[test]
904 fn account_status_response_error() {
905 TOKIO_SHARED_RT.block_on(async {
906 let client = MockAccountApiClient { force_error: true };
907
908 let params = AccountStatusParams::builder().build().unwrap();
909
910 match client.account_status(params).await {
911 Ok(_) => panic!("Expected an error"),
912 Err(err) => {
913 assert_eq!(err.to_string(), "Connector client error: ResponseError");
914 }
915 }
916 });
917 }
918
919 #[test]
920 fn daily_account_snapshot_required_params_success() {
921 TOKIO_SHARED_RT.block_on(async {
922 let client = MockAccountApiClient { force_error: false };
923
924 let params = DailyAccountSnapshotParams::builder(DailyAccountSnapshotTypeEnum::Spot,).build().unwrap();
925
926 let resp_json: Value = serde_json::from_str(r#"{"code":200,"msg":"","snapshotVos":[{"data":{"balances":[{"asset":"BTC","free":"0.09905021","locked":"0.00000000"}],"totalAssetOfBtc":"0.09942700","marginLevel":"2748.02909813","totalLiabilityOfBtc":"0.00000100","totalNetAssetOfBtc":"0.00274750","userAssets":[{"asset":"XRP","borrowed":"0.00000000","free":"1.00000000","interest":"0.00000000","locked":"0.00000000","netAsset":"1.00000000"}],"assets":[{"asset":"USDT","marginBalance":"118.99782335","walletBalance":"120.23811389"}],"position":[{"entryPrice":"7130.41000000","markPrice":"7257.66239673","positionAmt":"0.01000000","symbol":"BTCUSDT","unRealizedProfit":"1.24029054"}]},"type":"spot","updateTime":1576281599000}]}"#).unwrap_or_else(|_| serde_json::json!({}));
927 let expected_response : models::DailyAccountSnapshotResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::DailyAccountSnapshotResponse");
928
929 let resp = client.daily_account_snapshot(params).await.expect("Expected a response");
930 let data_future = resp.data();
931 let actual_response = data_future.await.unwrap();
932 assert_eq!(actual_response, expected_response);
933 });
934 }
935
936 #[test]
937 fn daily_account_snapshot_optional_params_success() {
938 TOKIO_SHARED_RT.block_on(async {
939 let client = MockAccountApiClient { force_error: false };
940
941 let params = DailyAccountSnapshotParams::builder(DailyAccountSnapshotTypeEnum::Spot,).start_time(1623319461670).end_time(1641782889000).limit(7).recv_window(5000).build().unwrap();
942
943 let resp_json: Value = serde_json::from_str(r#"{"code":200,"msg":"","snapshotVos":[{"data":{"balances":[{"asset":"BTC","free":"0.09905021","locked":"0.00000000"}],"totalAssetOfBtc":"0.09942700","marginLevel":"2748.02909813","totalLiabilityOfBtc":"0.00000100","totalNetAssetOfBtc":"0.00274750","userAssets":[{"asset":"XRP","borrowed":"0.00000000","free":"1.00000000","interest":"0.00000000","locked":"0.00000000","netAsset":"1.00000000"}],"assets":[{"asset":"USDT","marginBalance":"118.99782335","walletBalance":"120.23811389"}],"position":[{"entryPrice":"7130.41000000","markPrice":"7257.66239673","positionAmt":"0.01000000","symbol":"BTCUSDT","unRealizedProfit":"1.24029054"}]},"type":"spot","updateTime":1576281599000}]}"#).unwrap_or_else(|_| serde_json::json!({}));
944 let expected_response : models::DailyAccountSnapshotResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::DailyAccountSnapshotResponse");
945
946 let resp = client.daily_account_snapshot(params).await.expect("Expected a response");
947 let data_future = resp.data();
948 let actual_response = data_future.await.unwrap();
949 assert_eq!(actual_response, expected_response);
950 });
951 }
952
953 #[test]
954 fn daily_account_snapshot_response_error() {
955 TOKIO_SHARED_RT.block_on(async {
956 let client = MockAccountApiClient { force_error: true };
957
958 let params = DailyAccountSnapshotParams::builder(DailyAccountSnapshotTypeEnum::Spot)
959 .build()
960 .unwrap();
961
962 match client.daily_account_snapshot(params).await {
963 Ok(_) => panic!("Expected an error"),
964 Err(err) => {
965 assert_eq!(err.to_string(), "Connector client error: ResponseError");
966 }
967 }
968 });
969 }
970
971 #[test]
972 fn disable_fast_withdraw_switch_required_params_success() {
973 TOKIO_SHARED_RT.block_on(async {
974 let client = MockAccountApiClient { force_error: false };
975
976 let params = DisableFastWithdrawSwitchParams::builder().build().unwrap();
977
978 let expected_response = Value::Null;
979
980 let resp = client
981 .disable_fast_withdraw_switch(params)
982 .await
983 .expect("Expected a response");
984 let data_future = resp.data();
985 let actual_response = data_future.await.unwrap();
986 assert_eq!(actual_response, expected_response);
987 });
988 }
989
990 #[test]
991 fn disable_fast_withdraw_switch_optional_params_success() {
992 TOKIO_SHARED_RT.block_on(async {
993 let client = MockAccountApiClient { force_error: false };
994
995 let params = DisableFastWithdrawSwitchParams::builder()
996 .recv_window(5000)
997 .build()
998 .unwrap();
999
1000 let expected_response = Value::Null;
1001
1002 let resp = client
1003 .disable_fast_withdraw_switch(params)
1004 .await
1005 .expect("Expected a response");
1006 let data_future = resp.data();
1007 let actual_response = data_future.await.unwrap();
1008 assert_eq!(actual_response, expected_response);
1009 });
1010 }
1011
1012 #[test]
1013 fn disable_fast_withdraw_switch_response_error() {
1014 TOKIO_SHARED_RT.block_on(async {
1015 let client = MockAccountApiClient { force_error: true };
1016
1017 let params = DisableFastWithdrawSwitchParams::builder().build().unwrap();
1018
1019 match client.disable_fast_withdraw_switch(params).await {
1020 Ok(_) => panic!("Expected an error"),
1021 Err(err) => {
1022 assert_eq!(err.to_string(), "Connector client error: ResponseError");
1023 }
1024 }
1025 });
1026 }
1027
1028 #[test]
1029 fn enable_fast_withdraw_switch_required_params_success() {
1030 TOKIO_SHARED_RT.block_on(async {
1031 let client = MockAccountApiClient { force_error: false };
1032
1033 let params = EnableFastWithdrawSwitchParams::builder().build().unwrap();
1034
1035 let expected_response = Value::Null;
1036
1037 let resp = client
1038 .enable_fast_withdraw_switch(params)
1039 .await
1040 .expect("Expected a response");
1041 let data_future = resp.data();
1042 let actual_response = data_future.await.unwrap();
1043 assert_eq!(actual_response, expected_response);
1044 });
1045 }
1046
1047 #[test]
1048 fn enable_fast_withdraw_switch_optional_params_success() {
1049 TOKIO_SHARED_RT.block_on(async {
1050 let client = MockAccountApiClient { force_error: false };
1051
1052 let params = EnableFastWithdrawSwitchParams::builder()
1053 .recv_window(5000)
1054 .build()
1055 .unwrap();
1056
1057 let expected_response = Value::Null;
1058
1059 let resp = client
1060 .enable_fast_withdraw_switch(params)
1061 .await
1062 .expect("Expected a response");
1063 let data_future = resp.data();
1064 let actual_response = data_future.await.unwrap();
1065 assert_eq!(actual_response, expected_response);
1066 });
1067 }
1068
1069 #[test]
1070 fn enable_fast_withdraw_switch_response_error() {
1071 TOKIO_SHARED_RT.block_on(async {
1072 let client = MockAccountApiClient { force_error: true };
1073
1074 let params = EnableFastWithdrawSwitchParams::builder().build().unwrap();
1075
1076 match client.enable_fast_withdraw_switch(params).await {
1077 Ok(_) => panic!("Expected an error"),
1078 Err(err) => {
1079 assert_eq!(err.to_string(), "Connector client error: ResponseError");
1080 }
1081 }
1082 });
1083 }
1084
1085 #[test]
1086 fn get_api_key_permission_required_params_success() {
1087 TOKIO_SHARED_RT.block_on(async {
1088 let client = MockAccountApiClient { force_error: false };
1089
1090 let params = GetApiKeyPermissionParams::builder().build().unwrap();
1091
1092 let resp_json: Value = serde_json::from_str(r#"{"ipRestrict":false,"createTime":1623840271000,"enableReading":true,"enableWithdrawals":false,"enableInternalTransfer":true,"enableMargin":false,"enableFutures":false,"permitsUniversalTransfer":true,"enableVanillaOptions":false,"enableFixApiTrade":false,"enableFixReadOnly":true,"enableSpotAndMarginTrading":false,"enablePortfolioMarginTrading":true}"#).unwrap_or_else(|_| serde_json::json!({}));
1093 let expected_response : models::GetApiKeyPermissionResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::GetApiKeyPermissionResponse");
1094
1095 let resp = client.get_api_key_permission(params).await.expect("Expected a response");
1096 let data_future = resp.data();
1097 let actual_response = data_future.await.unwrap();
1098 assert_eq!(actual_response, expected_response);
1099 });
1100 }
1101
1102 #[test]
1103 fn get_api_key_permission_optional_params_success() {
1104 TOKIO_SHARED_RT.block_on(async {
1105 let client = MockAccountApiClient { force_error: false };
1106
1107 let params = GetApiKeyPermissionParams::builder().recv_window(5000).build().unwrap();
1108
1109 let resp_json: Value = serde_json::from_str(r#"{"ipRestrict":false,"createTime":1623840271000,"enableReading":true,"enableWithdrawals":false,"enableInternalTransfer":true,"enableMargin":false,"enableFutures":false,"permitsUniversalTransfer":true,"enableVanillaOptions":false,"enableFixApiTrade":false,"enableFixReadOnly":true,"enableSpotAndMarginTrading":false,"enablePortfolioMarginTrading":true}"#).unwrap_or_else(|_| serde_json::json!({}));
1110 let expected_response : models::GetApiKeyPermissionResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::GetApiKeyPermissionResponse");
1111
1112 let resp = client.get_api_key_permission(params).await.expect("Expected a response");
1113 let data_future = resp.data();
1114 let actual_response = data_future.await.unwrap();
1115 assert_eq!(actual_response, expected_response);
1116 });
1117 }
1118
1119 #[test]
1120 fn get_api_key_permission_response_error() {
1121 TOKIO_SHARED_RT.block_on(async {
1122 let client = MockAccountApiClient { force_error: true };
1123
1124 let params = GetApiKeyPermissionParams::builder().build().unwrap();
1125
1126 match client.get_api_key_permission(params).await {
1127 Ok(_) => panic!("Expected an error"),
1128 Err(err) => {
1129 assert_eq!(err.to_string(), "Connector client error: ResponseError");
1130 }
1131 }
1132 });
1133 }
1134}