openlark_security/security/acs/v1/users/
mod.rs1use openlark_core::error::api_error;
6use std::sync::Arc;
7
8#[derive(Debug)]
10pub struct UsersService {
11 config: Arc<crate::models::SecurityConfig>,
12}
13
14impl UsersService {
15 pub fn new(config: Arc<crate::models::SecurityConfig>) -> Self {
17 Self { config }
18 }
19
20 pub fn get(&self) -> GetUserBuilder {
22 GetUserBuilder {
23 config: self.config.clone(),
24 user_id: String::new(),
25 }
26 }
27
28 pub fn list(&self) -> ListUsersBuilder {
30 ListUsersBuilder {
31 config: self.config.clone(),
32 page_size: Some(20),
33 page_token: None,
34 department_id: None,
35 status: None,
36 }
37 }
38
39 pub fn patch(&self) -> PatchUserBuilder {
41 PatchUserBuilder {
42 config: self.config.clone(),
43 user_id: String::new(),
44 name: None,
45 email: None,
46 mobile: None,
47 department_ids: None,
48 status: None,
49 rule_ids: None,
50 }
51 }
52}
53
54#[derive(Debug)]
56pub struct GetUserBuilder {
57 config: Arc<crate::models::SecurityConfig>,
58 user_id: String,
59}
60
61impl GetUserBuilder {
62 pub fn user_id(mut self, user_id: impl Into<String>) -> Self {
64 self.user_id = user_id.into();
65 self
66 }
67
68 pub async fn send(self) -> crate::SecurityResult<crate::models::acs::UserInfo> {
70 let url = format!(
71 "{}/open-apis/acs/v1/users/{}",
72 self.config.base_url, self.user_id
73 );
74
75 let response = reqwest::Client::new()
76 .get(&url)
77 .header(
78 "Authorization",
79 format!("Bearer {}", get_app_token(&self.config).await?),
80 )
81 .header("Content-Type", "application/json")
82 .send()
83 .await?;
84
85 if response.status().is_success() {
86 let api_response: crate::models::ApiResponse<crate::models::acs::UserInfo> =
87 response.json().await?;
88 match api_response.data {
89 Some(user) => Ok(user),
90 None => Err(api_error(
91 api_response.code as u16,
92 "/acs/v1/users",
93 &api_response.msg,
94 None,
95 )),
96 }
97 } else {
98 Err(api_error(
99 response.status().as_u16(),
100 "/acs/v1/users",
101 format!("HTTP: {}", response.status()),
102 None,
103 ))
104 }
105 }
106}
107
108#[derive(Debug)]
110pub struct ListUsersBuilder {
111 config: Arc<crate::models::SecurityConfig>,
112 page_size: Option<i32>,
113 page_token: Option<String>,
114 department_id: Option<String>,
115 status: Option<crate::models::Status>,
116}
117
118impl ListUsersBuilder {
119 pub fn page_size(mut self, page_size: i32) -> Self {
121 self.page_size = Some(page_size);
122 self
123 }
124
125 pub fn page_token(mut self, page_token: impl Into<String>) -> Self {
127 self.page_token = Some(page_token.into());
128 self
129 }
130
131 pub fn department_id(mut self, department_id: impl Into<String>) -> Self {
133 self.department_id = Some(department_id.into());
134 self
135 }
136
137 pub fn status(mut self, status: crate::models::Status) -> Self {
139 self.status = Some(status);
140 self
141 }
142
143 pub async fn send(self) -> crate::SecurityResult<crate::models::acs::UserListResponse> {
145 let mut url = format!("{}/open-apis/acs/v1/users", self.config.base_url);
146 let mut query_params = Vec::new();
147
148 if let Some(page_size) = self.page_size {
149 query_params.push(format!("page_size={page_size}"));
150 }
151 if let Some(page_token) = &self.page_token {
152 query_params.push(format!("page_token={page_token}"));
153 }
154 if let Some(department_id) = &self.department_id {
155 query_params.push(format!("department_id={department_id}"));
156 }
157 if let Some(status) = &self.status {
158 query_params.push(format!(
159 "status={}",
160 serde_json::to_string(status).unwrap_or_default()
161 ));
162 }
163
164 if !query_params.is_empty() {
165 url.push_str(&format!("?{}", query_params.join("&")));
166 }
167
168 let response = reqwest::Client::new()
169 .get(&url)
170 .header(
171 "Authorization",
172 format!("Bearer {}", get_app_token(&self.config).await?),
173 )
174 .header("Content-Type", "application/json")
175 .send()
176 .await?;
177
178 if response.status().is_success() {
179 let api_response: crate::models::ApiResponse<crate::models::acs::UserListResponse> =
180 response.json().await?;
181 match api_response.data {
182 Some(users) => Ok(users),
183 None => Err(api_error(
184 api_response.code as u16,
185 "/acs/v1/users",
186 &api_response.msg,
187 None,
188 )),
189 }
190 } else {
191 Err(api_error(
192 response.status().as_u16(),
193 "/acs/v1/users",
194 format!("HTTP: {}", response.status()),
195 None,
196 ))
197 }
198 }
199}
200
201#[derive(Debug)]
203pub struct PatchUserBuilder {
204 config: Arc<crate::models::SecurityConfig>,
205 user_id: String,
206 name: Option<String>,
207 email: Option<String>,
208 mobile: Option<String>,
209 department_ids: Option<Vec<String>>,
210 status: Option<crate::models::Status>,
211 rule_ids: Option<Vec<String>>,
212}
213
214impl PatchUserBuilder {
215 pub fn user_id(mut self, user_id: impl Into<String>) -> Self {
217 self.user_id = user_id.into();
218 self
219 }
220
221 pub fn name(mut self, name: impl Into<String>) -> Self {
223 self.name = Some(name.into());
224 self
225 }
226
227 pub fn email(mut self, email: impl Into<String>) -> Self {
229 self.email = Some(email.into());
230 self
231 }
232
233 pub fn mobile(mut self, mobile: impl Into<String>) -> Self {
235 self.mobile = Some(mobile.into());
236 self
237 }
238
239 pub fn department_ids(mut self, department_ids: Vec<String>) -> Self {
241 self.department_ids = Some(department_ids);
242 self
243 }
244
245 pub fn status(mut self, status: crate::models::Status) -> Self {
247 self.status = Some(status);
248 self
249 }
250
251 pub fn rule_ids(mut self, rule_ids: Vec<String>) -> Self {
253 self.rule_ids = Some(rule_ids);
254 self
255 }
256
257 pub async fn send(self) -> crate::SecurityResult<crate::models::acs::UserInfo> {
259 let url = format!(
260 "{}/open-apis/acs/v1/users/{}",
261 self.config.base_url, self.user_id
262 );
263
264 let mut request_body = serde_json::Map::new();
265
266 if let Some(name) = self.name {
267 request_body.insert("name".to_string(), serde_json::Value::String(name));
268 }
269 if let Some(email) = self.email {
270 request_body.insert("email".to_string(), serde_json::Value::String(email));
271 }
272 if let Some(mobile) = self.mobile {
273 request_body.insert("mobile".to_string(), serde_json::Value::String(mobile));
274 }
275 if let Some(department_ids) = self.department_ids {
276 request_body.insert(
277 "department_ids".to_string(),
278 serde_json::Value::Array(
279 department_ids
280 .into_iter()
281 .map(serde_json::Value::String)
282 .collect(),
283 ),
284 );
285 }
286 if let Some(status) = self.status {
287 request_body.insert(
288 "status".to_string(),
289 serde_json::to_value(status).unwrap_or(serde_json::Value::Null),
290 );
291 }
292 if let Some(rule_ids) = self.rule_ids {
293 request_body.insert(
294 "rule_ids".to_string(),
295 serde_json::Value::Array(
296 rule_ids
297 .into_iter()
298 .map(serde_json::Value::String)
299 .collect(),
300 ),
301 );
302 }
303
304 let response = reqwest::Client::new()
305 .patch(&url)
306 .header(
307 "Authorization",
308 format!("Bearer {}", get_app_token(&self.config).await?),
309 )
310 .header("Content-Type", "application/json")
311 .json(&request_body)
312 .send()
313 .await?;
314
315 if response.status().is_success() {
316 let api_response: crate::models::ApiResponse<crate::models::acs::UserInfo> =
317 response.json().await?;
318 match api_response.data {
319 Some(user) => Ok(user),
320 None => Err(api_error(
321 api_response.code as u16,
322 "/acs/v1/users",
323 &api_response.msg,
324 None,
325 )),
326 }
327 } else {
328 Err(api_error(
329 response.status().as_u16(),
330 "/acs/v1/users",
331 format!("HTTP: {}", response.status()),
332 None,
333 ))
334 }
335 }
336}
337
338async fn get_app_token(config: &crate::models::SecurityConfig) -> crate::SecurityResult<String> {
340 config.get_app_access_token().await
342}
343
344#[cfg(test)]
345mod tests {
346 use super::*;
347 use std::sync::Arc;
348
349 fn create_test_config() -> Arc<crate::models::SecurityConfig> {
350 Arc::new(crate::models::SecurityConfig {
351 app_id: "test_app_id".to_string(),
352 app_secret: "test_app_secret".to_string(),
353 base_url: "https://open.feishu.cn".to_string(),
354 })
355 }
356
357 #[test]
358 fn test_users_service_creation() {
359 let config = create_test_config();
360 let service = UsersService::new(config.clone());
361 assert_eq!(service.config.app_id, "test_app_id");
362 }
363
364 #[test]
365 fn test_get_user_builder() {
366 let config = create_test_config();
367 let service = UsersService::new(config);
368 let builder = service.get().user_id("user_123");
369 assert_eq!(builder.user_id, "user_123");
370 }
371
372 #[test]
373 fn test_list_users_builder_defaults() {
374 let config = create_test_config();
375 let service = UsersService::new(config);
376 let builder = service.list();
377 assert_eq!(builder.page_size, Some(20));
378 assert_eq!(builder.page_token, None);
379 assert_eq!(builder.department_id, None);
380 assert_eq!(builder.status, None);
381 }
382
383 #[test]
384 fn test_list_users_builder_with_params() {
385 let config = create_test_config();
386 let service = UsersService::new(config);
387 let builder = service
388 .list()
389 .page_size(50)
390 .page_token("token_123")
391 .department_id("dept_456")
392 .status(crate::models::Status::Active);
393
394 assert_eq!(builder.page_size, Some(50));
395 assert_eq!(builder.page_token, Some("token_123".to_string()));
396 assert_eq!(builder.department_id, Some("dept_456".to_string()));
397 assert!(builder.status.is_some());
398 }
399
400 #[test]
401 fn test_patch_user_builder() {
402 let config = create_test_config();
403 let service = UsersService::new(config);
404 let builder = service
405 .patch()
406 .user_id("user_789")
407 .name("张三")
408 .email("zhangsan@example.com")
409 .mobile("13800138000")
410 .department_ids(vec!["dept_1".to_string(), "dept_2".to_string()])
411 .status(crate::models::Status::Active)
412 .rule_ids(vec!["rule_1".to_string()]);
413
414 assert_eq!(builder.user_id, "user_789");
415 assert_eq!(builder.name, Some("张三".to_string()));
416 assert_eq!(builder.email, Some("zhangsan@example.com".to_string()));
417 assert_eq!(builder.mobile, Some("13800138000".to_string()));
418 assert_eq!(
419 builder.department_ids,
420 Some(vec!["dept_1".to_string(), "dept_2".to_string()])
421 );
422 assert!(builder.status.is_some());
423 assert_eq!(builder.rule_ids, Some(vec!["rule_1".to_string()]));
424 }
425
426 #[test]
427 fn test_patch_user_builder_chaining() {
428 let config = create_test_config();
429 let service = UsersService::new(config);
430 let builder = service
431 .patch()
432 .user_id("user_123")
433 .name("李四")
434 .email("lisi@example.com");
435
436 assert_eq!(builder.user_id, "user_123");
437 assert_eq!(builder.name, Some("李四".to_string()));
438 assert_eq!(builder.email, Some("lisi@example.com".to_string()));
439 assert!(builder.mobile.is_none()); }
441}