open_lark/service/authentication/v1/
auth.rs1use crate::core::{
2 api_req::ApiRequest,
3 api_resp::{ApiResponseTrait, BaseResponse, ResponseFormat},
4 config::Config,
5 constants::AccessTokenType,
6 endpoints::auth::*,
7 http::Transport,
8 req_option::RequestOption,
9 standard_response::StandardResponse,
10 trait_system::Service,
11 SDKResult,
12};
13use serde::{Deserialize, Serialize};
14
15pub struct UserInfoService {
16 config: Config,
17}
18
19impl UserInfoService {
20 pub fn new(config: Config) -> Self {
21 Self { config }
22 }
23
24 pub async fn get(&self, user_access_token: impl ToString) -> SDKResult<UserInfo> {
28 let api_req = ApiRequest {
29 api_path: AUTHEN_V1_USER_INFO.to_string(),
30 supported_access_token_types: vec![AccessTokenType::Tenant, AccessTokenType::User],
31 ..Default::default()
32 };
33
34 let option = RequestOption::builder()
35 .user_access_token(user_access_token)
36 .build();
37 let api_resp: BaseResponse<UserInfo> =
38 Transport::request(api_req, &self.config, Some(option)).await?;
39 api_resp.into_result()
40 }
41}
42
43#[derive(Debug, Deserialize, Serialize)]
45pub struct UserInfo {
46 pub name: String,
48 pub en_name: String,
50 pub avatar_url: String,
52 pub avatar_thumb: String,
54 pub avatar_middle: String,
56 pub avatar_big: String,
58 pub open_id: String,
60 pub union_id: String,
62 pub email: Option<String>,
64 pub enterprise_email: Option<String>,
66 pub user_id: String,
68 pub mobile: Option<String>,
70 pub tenant_key: String,
72 pub employee_no: String,
74}
75
76impl ApiResponseTrait for UserInfo {
77 fn data_format() -> ResponseFormat {
78 ResponseFormat::Data
79 }
80}
81
82impl Service for UserInfoService {
83 fn config(&self) -> &Config {
84 &self.config
85 }
86
87 fn service_name() -> &'static str {
88 "user_info"
89 }
90
91 fn service_version() -> &'static str {
92 "v1"
93 }
94}
95
96#[cfg(test)]
97#[allow(unused_variables, unused_unsafe)]
98mod tests {
99 use super::*;
100 use crate::core::config::Config;
101
102 #[test]
103 fn test_user_info_deserialization() {
104 let json_str = r#"{
105 "name": "zhangsan",
106 "en_name": "zhangsan",
107 "avatar_url": "www.feishu.cn/avatar/icon",
108 "avatar_thumb": "www.feishu.cn/avatar/icon_thumb",
109 "avatar_middle": "www.feishu.cn/avatar/icon_middle",
110 "avatar_big": "www.feishu.cn/avatar/icon_big",
111 "open_id": "ou-caecc734c2e3328a62489fe0648c4b98779515d3",
112 "union_id": "on-d89jhsdhjsajkda7828enjdj328ydhhw3u43yjhdj",
113 "email": "zhangsan@feishu.cn",
114 "enterprise_email": "demo@mail.com",
115 "user_id": "5d9bdxxx",
116 "mobile": "+86130002883xx",
117 "tenant_key": "736588c92lxf175d",
118 "employee_no": "111222333"
119 }"#;
120
121 let user_info: UserInfo =
122 serde_json::from_str(json_str).expect("Failed to parse test user info JSON");
123
124 assert_eq!(user_info.name, "zhangsan");
125 assert_eq!(user_info.en_name, "zhangsan");
126 assert_eq!(user_info.avatar_url, "www.feishu.cn/avatar/icon");
127 assert_eq!(user_info.avatar_thumb, "www.feishu.cn/avatar/icon_thumb");
128 assert_eq!(user_info.avatar_middle, "www.feishu.cn/avatar/icon_middle");
129 assert_eq!(user_info.avatar_big, "www.feishu.cn/avatar/icon_big");
130 assert_eq!(
131 user_info.open_id,
132 "ou-caecc734c2e3328a62489fe0648c4b98779515d3"
133 );
134 assert_eq!(
135 user_info.union_id,
136 "on-d89jhsdhjsajkda7828enjdj328ydhhw3u43yjhdj"
137 );
138 assert_eq!(user_info.email, Some("zhangsan@feishu.cn".to_string()));
139 assert_eq!(
140 user_info.enterprise_email,
141 Some("demo@mail.com".to_string())
142 );
143 assert_eq!(user_info.user_id, "5d9bdxxx");
144 assert_eq!(user_info.mobile, Some("+86130002883xx".to_string()));
145 assert_eq!(user_info.tenant_key, "736588c92lxf175d");
146 assert_eq!(user_info.employee_no, "111222333");
147 }
148
149 #[test]
150 fn test_user_info_optional_fields() {
151 let json_str = r#"{
152 "name": "testuser",
153 "en_name": "testuser",
154 "avatar_url": "www.feishu.cn/avatar/icon",
155 "avatar_thumb": "www.feishu.cn/avatar/icon_thumb",
156 "avatar_middle": "www.feishu.cn/avatar/icon_middle",
157 "avatar_big": "www.feishu.cn/avatar/icon_big",
158 "open_id": "ou-test123456789",
159 "union_id": "on-test123456789",
160 "user_id": "test123",
161 "tenant_key": "test_tenant",
162 "employee_no": "EMP001"
163 }"#;
164
165 let user_info: UserInfo = serde_json::from_str(json_str).unwrap();
166
167 assert_eq!(user_info.name, "testuser");
168 assert_eq!(user_info.user_id, "test123");
169 assert!(user_info.email.is_none());
170 assert!(user_info.enterprise_email.is_none());
171 assert!(user_info.mobile.is_none());
172 }
173
174 #[test]
175 fn test_user_info_service_new() {
176 let config = Config::default();
177 let service = UserInfoService::new(config.clone());
178
179 assert_eq!(service.config.base_url, config.base_url);
181 assert_eq!(service.config.app_id, config.app_id);
182 assert_eq!(service.config.app_secret, config.app_secret);
183 }
184
185 #[test]
186 fn test_user_info_api_response_trait() {
187 let format = UserInfo::data_format();
189 assert!(matches!(format, ResponseFormat::Data));
192 }
193
194 #[test]
195 fn test_user_info_debug_trait() {
196 let user_info = UserInfo {
197 name: "test".to_string(),
198 en_name: "test".to_string(),
199 avatar_url: "url".to_string(),
200 avatar_thumb: "thumb".to_string(),
201 avatar_middle: "middle".to_string(),
202 avatar_big: "big".to_string(),
203 open_id: "open_id".to_string(),
204 union_id: "union_id".to_string(),
205 email: Some("test@example.com".to_string()),
206 enterprise_email: None,
207 user_id: "user_id".to_string(),
208 mobile: None,
209 tenant_key: "tenant".to_string(),
210 employee_no: "emp001".to_string(),
211 };
212
213 let debug_str = format!("{:?}", user_info);
214 assert!(debug_str.contains("test"));
215 assert!(debug_str.contains("UserInfo"));
216 }
217
218 #[test]
219 fn test_user_info_serde_round_trip() {
220 let original = UserInfo {
221 name: "test user".to_string(),
222 en_name: "test_user".to_string(),
223 avatar_url: "https://example.com/avatar.jpg".to_string(),
224 avatar_thumb: "https://example.com/avatar_thumb.jpg".to_string(),
225 avatar_middle: "https://example.com/avatar_middle.jpg".to_string(),
226 avatar_big: "https://example.com/avatar_big.jpg".to_string(),
227 open_id: "ou-12345".to_string(),
228 union_id: "on-67890".to_string(),
229 email: Some("test@company.com".to_string()),
230 enterprise_email: Some("test@enterprise.com".to_string()),
231 user_id: "u12345".to_string(),
232 mobile: Some("+1234567890".to_string()),
233 tenant_key: "tenant123".to_string(),
234 employee_no: "E12345".to_string(),
235 };
236
237 let json = serde_json::to_string(&original).unwrap();
239
240 let deserialized: UserInfo = serde_json::from_str(&json).unwrap();
242
243 assert_eq!(original.name, deserialized.name);
244 assert_eq!(original.en_name, deserialized.en_name);
245 assert_eq!(original.avatar_url, deserialized.avatar_url);
246 assert_eq!(original.open_id, deserialized.open_id);
247 assert_eq!(original.union_id, deserialized.union_id);
248 assert_eq!(original.email, deserialized.email);
249 assert_eq!(original.enterprise_email, deserialized.enterprise_email);
250 assert_eq!(original.user_id, deserialized.user_id);
251 assert_eq!(original.mobile, deserialized.mobile);
252 assert_eq!(original.tenant_key, deserialized.tenant_key);
253 assert_eq!(original.employee_no, deserialized.employee_no);
254 }
255
256 #[test]
257 fn test_user_info_with_unicode_characters() {
258 let json_str = r#"{
259 "name": "张三",
260 "en_name": "zhangsan",
261 "avatar_url": "www.feishu.cn/avatar/icon",
262 "avatar_thumb": "www.feishu.cn/avatar/icon_thumb",
263 "avatar_middle": "www.feishu.cn/avatar/icon_middle",
264 "avatar_big": "www.feishu.cn/avatar/icon_big",
265 "open_id": "ou-test",
266 "union_id": "on-test",
267 "email": "张三@公司.com",
268 "user_id": "user123",
269 "tenant_key": "tenant",
270 "employee_no": "工号001"
271 }"#;
272
273 let user_info: UserInfo = serde_json::from_str(json_str).unwrap();
274
275 assert_eq!(user_info.name, "张三");
276 assert_eq!(user_info.email, Some("张三@公司.com".to_string()));
277 assert_eq!(user_info.employee_no, "工号001");
278 }
279
280 #[test]
281 fn test_user_info_invalid_json() {
282 let invalid_json = r#"{
283 "name": "test",
284 "invalid_field": "should_not_cause_error"
285 }"#;
286
287 let result = serde_json::from_str::<UserInfo>(invalid_json);
289 assert!(result.is_err());
290 }
291
292 #[test]
293 fn test_user_info_empty_string_fields() {
294 let json_str = r#"{
295 "name": "",
296 "en_name": "",
297 "avatar_url": "",
298 "avatar_thumb": "",
299 "avatar_middle": "",
300 "avatar_big": "",
301 "open_id": "",
302 "union_id": "",
303 "user_id": "",
304 "tenant_key": "",
305 "employee_no": ""
306 }"#;
307
308 let user_info: UserInfo = serde_json::from_str(json_str).unwrap();
309
310 assert_eq!(user_info.name, "");
311 assert_eq!(user_info.en_name, "");
312 assert_eq!(user_info.open_id, "");
313 assert!(user_info.email.is_none());
314 assert!(user_info.mobile.is_none());
315 }
316
317 #[test]
318 fn test_user_info_service_config_independence() {
319 let config1 = Config::builder()
320 .app_id("app1")
321 .app_secret("secret1")
322 .build();
323 let config2 = Config::builder()
324 .app_id("app2")
325 .app_secret("secret2")
326 .build();
327
328 let service1 = UserInfoService::new(config1);
329 let service2 = UserInfoService::new(config2);
330
331 assert_eq!(service1.config.app_id, "app1");
332 assert_eq!(service2.config.app_id, "app2");
333 assert_ne!(service1.config.app_id, service2.config.app_id);
334 }
335}