Skip to main content

openlark_core/
req_option.rs

1use std::collections::HashMap;
2
3/// 请求选项
4///
5/// 用于配置 API 请求的各种选项,如认证令牌、请求头等
6#[derive(Debug, Clone, Default)]
7pub struct RequestOption {
8    pub(crate) tenant_key: Option<String>,
9    pub(crate) user_access_token: Option<String>,
10    pub(crate) app_access_token: Option<String>,
11    pub(crate) tenant_access_token: Option<String>,
12    pub(crate) need_helpdesk_auth: bool,
13    pub(crate) request_id: Option<String>,
14    pub(crate) app_ticket: Option<String>,
15    pub(crate) file_upload: bool,
16    pub(crate) file_download: bool,
17    pub(crate) header: HashMap<String, String>,
18}
19
20impl RequestOption {
21    /// 创建请求选项构建器
22    pub fn builder() -> RequestOptionBuilder {
23        RequestOptionBuilder::default()
24    }
25}
26
27/// 请求选项构建器
28///
29/// 使用 Builder 模式方便配置请求选项
30#[derive(Default)]
31pub struct RequestOptionBuilder {
32    option: RequestOption,
33}
34
35impl RequestOptionBuilder {
36    /// 设置租户键
37    pub fn tenant_key(mut self, tenant_key: impl ToString) -> Self {
38        self.option.tenant_key = Some(tenant_key.to_string());
39        self
40    }
41
42    /// 设置用户访问令牌
43    pub fn user_access_token(mut self, user_access_token: impl ToString) -> Self {
44        self.option.user_access_token = Some(user_access_token.to_string());
45        self
46    }
47
48    /// 设置应用访问令牌
49    pub fn app_access_token(mut self, app_access_token: impl ToString) -> Self {
50        self.option.app_access_token = Some(app_access_token.to_string());
51        self
52    }
53
54    /// 设置租户访问令牌
55    pub fn tenant_access_token(mut self, tenant_access_token: impl ToString) -> Self {
56        self.option.tenant_access_token = Some(tenant_access_token.to_string());
57        self
58    }
59
60    /// 设置是否需要帮助台认证
61    pub fn need_helpdesk_auth(mut self, need_helpdesk_auth: bool) -> Self {
62        self.option.need_helpdesk_auth = need_helpdesk_auth;
63        self
64    }
65
66    /// 设置请求 ID
67    pub fn request_id(mut self, request_id: impl ToString) -> Self {
68        self.option.request_id = Some(request_id.to_string());
69        self
70    }
71
72    /// 设置应用票据
73    pub fn app_ticket(mut self, app_ticket: impl ToString) -> Self {
74        self.option.app_ticket = Some(app_ticket.to_string());
75        self
76    }
77
78    /// 设置是否上传文件
79    pub fn file_upload(mut self, file_upload: bool) -> Self {
80        self.option.file_upload = file_upload;
81        self
82    }
83
84    /// 设置是否下载文件
85    pub fn file_download(mut self, file_download: bool) -> Self {
86        self.option.file_download = file_download;
87        self
88    }
89
90    /// 设置请求头
91    pub fn header(mut self, header: HashMap<String, String>) -> Self {
92        self.option.header = header;
93        self
94    }
95
96    /// 添加请求头
97    pub fn add_header(mut self, key: impl ToString, value: impl ToString) -> Self {
98        self.option
99            .header
100            .insert(key.to_string(), value.to_string());
101        self
102    }
103
104    /// 构建 RequestOption
105    pub fn build(self) -> RequestOption {
106        self.option
107    }
108}
109
110#[cfg(test)]
111mod tests {
112    use super::*;
113    use std::collections::HashMap;
114
115    #[test]
116    fn test_request_option_default() {
117        let option = RequestOption::default();
118        assert_eq!(option.tenant_key, None);
119        assert_eq!(option.user_access_token, None);
120        assert_eq!(option.app_access_token, None);
121        assert_eq!(option.tenant_access_token, None);
122        assert!(!option.need_helpdesk_auth);
123        assert_eq!(option.request_id, None);
124        assert_eq!(option.app_ticket, None);
125        assert!(!option.file_upload);
126        assert!(!option.file_download);
127        assert!(option.header.is_empty());
128    }
129
130    #[test]
131    fn test_request_option_builder_creation() {
132        let builder = RequestOption::builder();
133        let option = builder.build();
134
135        // Should be same as default
136        assert_eq!(option.tenant_key, None);
137        assert_eq!(option.user_access_token, None);
138        assert_eq!(option.app_access_token, None);
139        assert_eq!(option.tenant_access_token, None);
140        assert!(!option.need_helpdesk_auth);
141        assert_eq!(option.request_id, None);
142        assert_eq!(option.app_ticket, None);
143        assert!(!option.file_upload);
144        assert!(!option.file_download);
145        assert!(option.header.is_empty());
146    }
147
148    #[test]
149    fn test_request_option_builder_tenant_key() {
150        let option = RequestOption::builder().tenant_key("test_tenant").build();
151
152        assert_eq!(option.tenant_key, Some("test_tenant".to_string()));
153
154        // Test with String
155        let option = RequestOption::builder()
156            .tenant_key("another_tenant".to_string())
157            .build();
158
159        assert_eq!(option.tenant_key, Some("another_tenant".to_string()));
160    }
161
162    #[test]
163    fn test_request_option_builder_user_access_token() {
164        let option = RequestOption::builder()
165            .user_access_token("user_token_123")
166            .build();
167
168        assert_eq!(option.user_access_token, Some("user_token_123".to_string()));
169
170        // Test with String
171        let option = RequestOption::builder()
172            .user_access_token("user_token_456".to_string())
173            .build();
174
175        assert_eq!(option.user_access_token, Some("user_token_456".to_string()));
176    }
177
178    #[test]
179    fn test_request_option_builder_app_access_token() {
180        let option = RequestOption::builder()
181            .app_access_token("app_token_789")
182            .build();
183
184        assert_eq!(option.app_access_token, Some("app_token_789".to_string()));
185
186        // Test with String
187        let option = RequestOption::builder()
188            .app_access_token("app_token_012".to_string())
189            .build();
190
191        assert_eq!(option.app_access_token, Some("app_token_012".to_string()));
192    }
193
194    #[test]
195    fn test_request_option_builder_tenant_access_token() {
196        let option = RequestOption::builder()
197            .tenant_access_token("tenant_token_345")
198            .build();
199
200        assert_eq!(
201            option.tenant_access_token,
202            Some("tenant_token_345".to_string())
203        );
204
205        // Test with String
206        let option = RequestOption::builder()
207            .tenant_access_token("tenant_token_678".to_string())
208            .build();
209
210        assert_eq!(
211            option.tenant_access_token,
212            Some("tenant_token_678".to_string())
213        );
214    }
215
216    #[test]
217    fn test_request_option_builder_need_helpdesk_auth() {
218        let option = RequestOption::builder().need_helpdesk_auth(true).build();
219
220        assert!(option.need_helpdesk_auth);
221
222        let option = RequestOption::builder().need_helpdesk_auth(false).build();
223
224        assert!(!option.need_helpdesk_auth);
225    }
226
227    #[test]
228    fn test_request_option_builder_request_id() {
229        let option = RequestOption::builder().request_id("req_12345").build();
230
231        assert_eq!(option.request_id, Some("req_12345".to_string()));
232
233        // Test with String
234        let option = RequestOption::builder()
235            .request_id("req_67890".to_string())
236            .build();
237
238        assert_eq!(option.request_id, Some("req_67890".to_string()));
239    }
240
241    #[test]
242    fn test_request_option_builder_app_ticket() {
243        let option = RequestOption::builder().app_ticket("ticket_abc").build();
244
245        assert_eq!(option.app_ticket, Some("ticket_abc".to_string()));
246
247        // Test with String
248        let option = RequestOption::builder()
249            .app_ticket("ticket_def".to_string())
250            .build();
251
252        assert_eq!(option.app_ticket, Some("ticket_def".to_string()));
253    }
254
255    #[test]
256    fn test_request_option_builder_file_upload() {
257        let option = RequestOption::builder().file_upload(true).build();
258
259        assert!(option.file_upload);
260
261        let option = RequestOption::builder().file_upload(false).build();
262
263        assert!(!option.file_upload);
264    }
265
266    #[test]
267    fn test_request_option_builder_file_download() {
268        let option = RequestOption::builder().file_download(true).build();
269
270        assert!(option.file_download);
271
272        let option = RequestOption::builder().file_download(false).build();
273
274        assert!(!option.file_download);
275    }
276
277    #[test]
278    fn test_request_option_builder_header() {
279        let mut headers = HashMap::new();
280        headers.insert("Content-Type".to_string(), "application/json".to_string());
281        headers.insert("Authorization".to_string(), "Bearer token".to_string());
282
283        let option = RequestOption::builder().header(headers.clone()).build();
284
285        assert_eq!(option.header.len(), 2);
286        assert_eq!(
287            option.header.get("Content-Type"),
288            Some(&"application/json".to_string())
289        );
290        assert_eq!(
291            option.header.get("Authorization"),
292            Some(&"Bearer token".to_string())
293        );
294    }
295
296    #[test]
297    fn test_request_option_builder_add_header() {
298        let option = RequestOption::builder()
299            .add_header("X-Custom-Header", "custom_value")
300            .add_header("X-Another-Header", "another_value")
301            .build();
302
303        assert_eq!(option.header.len(), 2);
304        assert_eq!(
305            option.header.get("X-Custom-Header"),
306            Some(&"custom_value".to_string())
307        );
308        assert_eq!(
309            option.header.get("X-Another-Header"),
310            Some(&"another_value".to_string())
311        );
312
313        // Test with String types
314        let option = RequestOption::builder()
315            .add_header("X-String-Header".to_string(), "string_value".to_string())
316            .build();
317
318        assert_eq!(option.header.len(), 1);
319        assert_eq!(
320            option.header.get("X-String-Header"),
321            Some(&"string_value".to_string())
322        );
323    }
324
325    #[test]
326    fn test_request_option_builder_add_header_multiple() {
327        let option = RequestOption::builder()
328            .add_header("Header1", "value1")
329            .add_header("Header2", "value2")
330            .add_header("Header3", "value3")
331            .build();
332
333        assert_eq!(option.header.len(), 3);
334        assert_eq!(option.header.get("Header1"), Some(&"value1".to_string()));
335        assert_eq!(option.header.get("Header2"), Some(&"value2".to_string()));
336        assert_eq!(option.header.get("Header3"), Some(&"value3".to_string()));
337    }
338
339    #[test]
340    fn test_request_option_builder_header_and_add_header() {
341        let mut initial_headers = HashMap::new();
342        initial_headers.insert("Initial-Header".to_string(), "initial_value".to_string());
343
344        let option = RequestOption::builder()
345            .header(initial_headers)
346            .add_header("Added-Header", "added_value")
347            .build();
348
349        assert_eq!(option.header.len(), 2);
350        assert_eq!(
351            option.header.get("Initial-Header"),
352            Some(&"initial_value".to_string())
353        );
354        assert_eq!(
355            option.header.get("Added-Header"),
356            Some(&"added_value".to_string())
357        );
358    }
359
360    #[test]
361    fn test_request_option_builder_chaining() {
362        let option = RequestOption::builder()
363            .tenant_key("test_tenant")
364            .user_access_token("user_token")
365            .app_access_token("app_token")
366            .tenant_access_token("tenant_token")
367            .need_helpdesk_auth(true)
368            .request_id("req_123")
369            .app_ticket("ticket_456")
370            .file_upload(true)
371            .file_download(false)
372            .add_header("X-Test", "test_value")
373            .build();
374
375        assert_eq!(option.tenant_key, Some("test_tenant".to_string()));
376        assert_eq!(option.user_access_token, Some("user_token".to_string()));
377        assert_eq!(option.app_access_token, Some("app_token".to_string()));
378        assert_eq!(option.tenant_access_token, Some("tenant_token".to_string()));
379        assert!(option.need_helpdesk_auth);
380        assert_eq!(option.request_id, Some("req_123".to_string()));
381        assert_eq!(option.app_ticket, Some("ticket_456".to_string()));
382        assert!(option.file_upload);
383        assert!(!option.file_download);
384        assert_eq!(option.header.len(), 1);
385        assert_eq!(option.header.get("X-Test"), Some(&"test_value".to_string()));
386    }
387
388    #[test]
389    fn test_request_option_builder_empty_strings() {
390        let option = RequestOption::builder()
391            .tenant_key("")
392            .user_access_token("")
393            .app_access_token("")
394            .tenant_access_token("")
395            .request_id("")
396            .app_ticket("")
397            .build();
398
399        assert_eq!(option.tenant_key, Some("".to_string()));
400        assert_eq!(option.user_access_token, Some("".to_string()));
401        assert_eq!(option.app_access_token, Some("".to_string()));
402        assert_eq!(option.tenant_access_token, Some("".to_string()));
403        assert_eq!(option.request_id, Some("".to_string()));
404        assert_eq!(option.app_ticket, Some("".to_string()));
405    }
406
407    #[test]
408    fn test_request_option_builder_special_characters() {
409        let option = RequestOption::builder()
410            .tenant_key("tenant@#$%^&*()")
411            .user_access_token("token_with_symbols!@#")
412            .request_id("req_with_unicode_测试")
413            .add_header("X-Special-Chars", "value@#$%")
414            .build();
415
416        assert_eq!(option.tenant_key, Some("tenant@#$%^&*()".to_string()));
417        assert_eq!(
418            option.user_access_token,
419            Some("token_with_symbols!@#".to_string())
420        );
421        assert_eq!(option.request_id, Some("req_with_unicode_测试".to_string()));
422        assert_eq!(
423            option.header.get("X-Special-Chars"),
424            Some(&"value@#$%".to_string())
425        );
426    }
427
428    #[test]
429    fn test_request_option_builder_overwrite_header() {
430        let mut initial_headers = HashMap::new();
431        initial_headers.insert("Test-Header".to_string(), "initial_value".to_string());
432
433        let option = RequestOption::builder()
434            .header(initial_headers)
435            .add_header("Test-Header", "overwritten_value")
436            .build();
437
438        assert_eq!(option.header.len(), 1);
439        assert_eq!(
440            option.header.get("Test-Header"),
441            Some(&"overwritten_value".to_string())
442        );
443    }
444
445    #[test]
446    fn test_request_option_builder_empty_header_map() {
447        let empty_headers = HashMap::new();
448
449        let option = RequestOption::builder().header(empty_headers).build();
450
451        assert!(option.header.is_empty());
452    }
453
454    #[test]
455    fn test_request_option_debug_clone() {
456        let option = RequestOption::builder()
457            .tenant_key("test")
458            .need_helpdesk_auth(true)
459            .build();
460
461        // Test Debug trait
462        let debug_str = format!("{:?}", option);
463        assert!(debug_str.contains("RequestOption"));
464
465        // Test Clone trait
466        let cloned_option = option.clone();
467        assert_eq!(option.tenant_key, cloned_option.tenant_key);
468        assert_eq!(option.need_helpdesk_auth, cloned_option.need_helpdesk_auth);
469    }
470
471    #[test]
472    fn test_request_option_builder_default() {
473        let builder = RequestOptionBuilder::default();
474        let option = builder.build();
475
476        // Should be equivalent to RequestOption::default()
477        let default_option = RequestOption::default();
478
479        assert_eq!(option.tenant_key, default_option.tenant_key);
480        assert_eq!(option.user_access_token, default_option.user_access_token);
481        assert_eq!(option.app_access_token, default_option.app_access_token);
482        assert_eq!(
483            option.tenant_access_token,
484            default_option.tenant_access_token
485        );
486        assert_eq!(option.need_helpdesk_auth, default_option.need_helpdesk_auth);
487        assert_eq!(option.request_id, default_option.request_id);
488        assert_eq!(option.app_ticket, default_option.app_ticket);
489        assert_eq!(option.file_upload, default_option.file_upload);
490        assert_eq!(option.file_download, default_option.file_download);
491        assert_eq!(option.header.len(), default_option.header.len());
492    }
493}