bybit_rust_api/rest/user/
user_client.rs1use crate::rest::client::{RestClient, SecType, ServerResponse};
2use crate::rest::BybitResult as Result;
3use serde_json::json;
4
5#[derive(Clone)]
6pub struct UserClient {
7 client: RestClient,
8}
9
10impl UserClient {
11 pub fn new(client: RestClient) -> Self {
12 UserClient { client }
13 }
14
15 pub async fn create_sub_member(
20 &self,
21 username: &str,
22 member_type: i32, password: Option<&str>,
24 note: Option<&str>,
25 switch_option: Option<i32>,
26 is_uta: Option<bool>,
27 ) -> Result<ServerResponse<serde_json::Value>> {
28 let endpoint = "v5/user/create-sub-member";
29 let mut body = json!({
30 "username": username,
31 "memberType": member_type,
32 });
33
34 if let Some(password) = password {
35 body["password"] = json!(password);
36 }
37 if let Some(note) = note {
38 body["note"] = json!(note);
39 }
40 if let Some(switch_option) = switch_option {
41 body["switch"] = json!(switch_option);
42 }
43 if let Some(is_uta) = is_uta {
44 body["isUta"] = json!(is_uta);
45 }
46
47 let response = self.client.post(endpoint, body, SecType::Signed).await?;
48 Ok(response)
49 }
50
51 pub async fn create_sub_api(
56 &self,
57 sub_uid: i64,
58 note: Option<&str>,
59 read_only: i32,
60 permissions: serde_json::Value,
61 ips: Option<Vec<String>>,
62 ) -> Result<ServerResponse<serde_json::Value>> {
63 let endpoint = "v5/user/create-sub-api";
64 let mut body = json!({
65 "subuid": sub_uid,
66 "readOnly": read_only,
67 "permissions": permissions,
68 });
69
70 if let Some(note) = note {
71 body["note"] = json!(note);
72 }
73 if let Some(ips) = ips {
74 body["ips"] = json!(ips);
75 }
76
77 let response = self.client.post(endpoint, body, SecType::Signed).await?;
78 Ok(response)
79 }
80
81 pub async fn query_sub_members(
86 &self,
87 page_size: Option<i32>,
88 page: Option<i32>,
89 ) -> Result<ServerResponse<serde_json::Value>> {
90 let endpoint = "v5/user/query-sub-members";
91 let mut params = json!({});
92
93 if let Some(page_size) = page_size {
94 params["pageSize"] = json!(page_size);
95 }
96 if let Some(page) = page {
97 params["page"] = json!(page);
98 }
99
100 let response = self.client.get(endpoint, params, SecType::Signed).await?;
101 Ok(response)
102 }
103
104 pub async fn get_sub_members(
109 &self,
110 uid: Option<&str>,
111 ) -> Result<ServerResponse<serde_json::Value>> {
112 let endpoint = "v5/user/submembers";
113 let mut params = json!({});
114
115 if let Some(uid) = uid {
116 params["uid"] = json!(uid);
117 }
118
119 let response = self.client.get(endpoint, params, SecType::Signed).await?;
120 Ok(response)
121 }
122
123 pub async fn query_api(&self) -> Result<ServerResponse<serde_json::Value>> {
128 let endpoint = "v5/user/query-api";
129 let response = self
130 .client
131 .get(endpoint, json!({}), SecType::Signed)
132 .await?;
133 Ok(response)
134 }
135
136 pub async fn get_sub_api_keys(
141 &self,
142 sub_member_id: &str,
143 limit: Option<i32>,
144 cursor: Option<&str>,
145 ) -> Result<ServerResponse<serde_json::Value>> {
146 let endpoint = "v5/user/sub-apikeys";
147 let mut params = json!({
148 "subMemberId": sub_member_id,
149 });
150
151 if let Some(limit) = limit {
152 params["limit"] = json!(limit);
153 }
154 if let Some(cursor) = cursor {
155 params["cursor"] = json!(cursor);
156 }
157
158 let response = self.client.get(endpoint, params, SecType::Signed).await?;
159 Ok(response)
160 }
161
162 pub async fn get_member_type(&self) -> Result<ServerResponse<serde_json::Value>> {
167 let endpoint = "v5/user/get-member-type";
168 let response = self
169 .client
170 .get(endpoint, json!({}), SecType::Signed)
171 .await?;
172 Ok(response)
173 }
174
175 pub async fn get_affiliate_customer_info(
180 &self,
181 uid: &str,
182 ) -> Result<ServerResponse<serde_json::Value>> {
183 let endpoint = "v5/user/aff-customer-info";
184 let params = json!({
185 "uid": uid,
186 });
187
188 let response = self.client.get(endpoint, params, SecType::Signed).await?;
189 Ok(response)
190 }
191
192 pub async fn freeze_sub_member(
197 &self,
198 sub_uid: i64,
199 frozen: i32, ) -> Result<ServerResponse<serde_json::Value>> {
201 let endpoint = "v5/user/frozen-sub-member";
202 let body = json!({
203 "subuid": sub_uid,
204 "frozen": frozen,
205 });
206
207 let response = self.client.post(endpoint, body, SecType::Signed).await?;
208 Ok(response)
209 }
210
211 pub async fn delete_sub_member(
216 &self,
217 sub_member_id: &str,
218 ) -> Result<ServerResponse<serde_json::Value>> {
219 let endpoint = "v5/user/del-submember";
220 let body = json!({
221 "subMemberId": sub_member_id,
222 });
223
224 let response = self.client.post(endpoint, body, SecType::Signed).await?;
225 Ok(response)
226 }
227
228 pub async fn update_api(
233 &self,
234 read_only: Option<i32>,
235 ips: Option<Vec<String>>,
236 permissions: Option<serde_json::Value>,
237 ) -> Result<ServerResponse<serde_json::Value>> {
238 let endpoint = "v5/user/update-api";
239 let mut body = json!({});
240
241 if let Some(read_only) = read_only {
242 body["readOnly"] = json!(read_only);
243 }
244 if let Some(ips) = ips {
245 body["ips"] = json!(ips);
246 }
247 if let Some(permissions) = permissions {
248 body["permissions"] = json!(permissions);
249 }
250
251 let response = self.client.post(endpoint, body, SecType::Signed).await?;
252 Ok(response)
253 }
254
255 pub async fn delete_api(&self) -> Result<ServerResponse<serde_json::Value>> {
260 let endpoint = "v5/user/delete-api";
261 let body = json!({});
262
263 let response = self.client.post(endpoint, body, SecType::Signed).await?;
264 Ok(response)
265 }
266
267 pub async fn update_sub_api(
272 &self,
273 api_key: &str,
274 read_only: Option<i32>,
275 ips: Option<Vec<String>>,
276 permissions: Option<serde_json::Value>,
277 ) -> Result<ServerResponse<serde_json::Value>> {
278 let endpoint = "v5/user/update-sub-api";
279 let mut body = json!({
280 "apikey": api_key,
281 });
282
283 if let Some(read_only) = read_only {
284 body["readOnly"] = json!(read_only);
285 }
286 if let Some(ips) = ips {
287 body["ips"] = json!(ips);
288 }
289 if let Some(permissions) = permissions {
290 body["permissions"] = json!(permissions);
291 }
292
293 let response = self.client.post(endpoint, body, SecType::Signed).await?;
294 Ok(response)
295 }
296
297 pub async fn delete_sub_api(&self, api_key: &str) -> Result<ServerResponse<serde_json::Value>> {
302 let endpoint = "v5/user/delete-sub-api";
303 let body = json!({
304 "apikey": api_key,
305 });
306
307 let response = self.client.post(endpoint, body, SecType::Signed).await?;
308 Ok(response)
309 }
310}
311
312#[cfg(test)]
313mod tests {
314 use super::*;
315 use crate::rest::ApiKeyPair;
316
317 fn create_test_client() -> UserClient {
318 let api_key_pair = ApiKeyPair::new(
319 "test".to_string(),
320 "test_key".to_string(),
321 "test_secret".to_string(),
322 );
323 let rest_client =
324 RestClient::new(api_key_pair, "https://api-testnet.bybit.com".to_string());
325 UserClient::new(rest_client)
326 }
327
328 #[test]
329 fn test_user_client_creation() {
330 let _client = create_test_client();
331 }
332
333 #[tokio::test]
334 async fn test_create_sub_member_params() {
335 let username = "test_sub_user";
336 let member_type = 1;
337 let password = Some("secure_password");
338 let note = Some("Test sub account");
339 let switch_option = Some(1);
340 let is_uta = Some(true);
341
342 assert_eq!(username, "test_sub_user");
343 assert_eq!(member_type, 1);
344 assert_eq!(password, Some("secure_password"));
345 assert_eq!(note, Some("Test sub account"));
346 assert_eq!(switch_option, Some(1));
347 assert_eq!(is_uta, Some(true));
348 }
349
350 #[tokio::test]
351 async fn test_create_sub_api_params() {
352 let sub_uid = 12345678i64;
353 let note = Some("API for trading");
354 let read_only = 0;
355 let permissions = json!({
356 "ContractTrade": ["Order", "Position"],
357 "Spot": ["SpotTrade"]
358 });
359 let ips = Some(vec!["192.168.1.1".to_string(), "10.0.0.1".to_string()]);
360
361 assert_eq!(sub_uid, 12345678);
362 assert_eq!(note, Some("API for trading"));
363 assert_eq!(read_only, 0);
364 assert!(permissions.is_object());
365 assert_eq!(ips.as_ref().unwrap().len(), 2);
366 }
367
368 #[tokio::test]
369 async fn test_freeze_sub_member_params() {
370 let sub_uid = 87654321i64;
371 let frozen_freeze = 1;
372 let frozen_unfreeze = 0;
373
374 assert_eq!(sub_uid, 87654321);
375 assert_eq!(frozen_freeze, 1);
376 assert_eq!(frozen_unfreeze, 0);
377 }
378
379 #[tokio::test]
380 async fn test_query_sub_members_params() {
381 let page_size = Some(20);
382 let page = Some(1);
383
384 assert_eq!(page_size, Some(20));
385 assert_eq!(page, Some(1));
386 }
387
388 #[tokio::test]
389 async fn test_get_sub_api_keys_params() {
390 let sub_member_id = "sub_member_123";
391 let limit = Some(50);
392 let cursor = Some("next_page_cursor");
393
394 assert_eq!(sub_member_id, "sub_member_123");
395 assert_eq!(limit, Some(50));
396 assert_eq!(cursor, Some("next_page_cursor"));
397 }
398
399 #[tokio::test]
400 async fn test_update_api_params() {
401 let read_only = Some(1);
402 let ips = Some(vec!["10.0.0.1".to_string()]);
403 let permissions = Some(json!({
404 "Spot": ["SpotTrade"]
405 }));
406
407 assert_eq!(read_only, Some(1));
408 assert_eq!(ips.as_ref().unwrap().len(), 1);
409 assert!(permissions.as_ref().unwrap().is_object());
410 }
411
412 #[tokio::test]
413 async fn test_delete_sub_api_params() {
414 let api_key = "test_api_key_123";
415
416 assert_eq!(api_key, "test_api_key_123");
417 }
418}