Skip to main content

zoom_cli/commands/
users.rs

1use crate::api::{ApiError, ZoomClient};
2use crate::output::{self, OutputConfig};
3
4pub async fn list(
5    client: &mut ZoomClient,
6    out: &OutputConfig,
7    status: Option<&str>,
8) -> Result<(), ApiError> {
9    let result = client.list_users(status).await?;
10
11    if out.json {
12        out.print_data(&serde_json::to_string_pretty(&result).expect("serialize"));
13    } else {
14        if result.users.is_empty() {
15            out.print_message("No users found.");
16            return Ok(());
17        }
18        let rows: Vec<Vec<String>> = result
19            .users
20            .iter()
21            .map(|u| {
22                vec![
23                    u.id.clone(),
24                    u.email.clone(),
25                    u.display_name.clone().unwrap_or_default(),
26                    u.status.clone().unwrap_or_default(),
27                ]
28            })
29            .collect();
30        out.print_data(&output::table(&["ID", "EMAIL", "NAME", "STATUS"], &rows));
31        if let Some(total) = result.total_records {
32            out.print_message(&format!("{total} user(s) total"));
33        }
34    }
35    Ok(())
36}
37
38pub async fn get(
39    client: &mut ZoomClient,
40    out: &OutputConfig,
41    id_or_email: &str,
42) -> Result<(), ApiError> {
43    let user = client.get_user(id_or_email).await?;
44
45    if out.json {
46        out.print_data(&serde_json::to_string_pretty(&user).expect("serialize"));
47    } else {
48        let name = user
49            .display_name
50            .clone()
51            .or_else(|| match (&user.first_name, &user.last_name) {
52                (Some(f), Some(l)) => Some(format!("{f} {l}")),
53                (Some(f), None) => Some(f.clone()),
54                _ => None,
55            })
56            .unwrap_or_default();
57        out.print_data(&output::kv_block(&[
58            ("id", user.id.clone()),
59            ("email", user.email.clone()),
60            ("name", name),
61            ("status", user.status.clone().unwrap_or_default()),
62        ]));
63    }
64    Ok(())
65}
66
67pub async fn me(client: &mut ZoomClient, out: &OutputConfig) -> Result<(), ApiError> {
68    get(client, out, "me").await
69}
70
71#[cfg(test)]
72mod tests {
73    use super::*;
74    use crate::api::ZoomClient;
75    use wiremock::matchers::{method, path};
76    use wiremock::{Mock, MockServer, ResponseTemplate};
77
78    fn test_out_json() -> OutputConfig {
79        OutputConfig {
80            json: true,
81            quiet: true,
82        }
83    }
84
85    #[tokio::test]
86    async fn users_list_json_includes_total_records() {
87        let server = MockServer::start().await;
88        Mock::given(method("GET"))
89            .and(path("/v2/users"))
90            .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
91                "users": [
92                    {"id": "u1", "email": "a@test.com", "display_name": "Alice", "status": "active"}
93                ],
94                "total_records": 1
95            })))
96            .mount(&server)
97            .await;
98
99        let mut client =
100            ZoomClient::new_for_test(format!("{}/v2", server.uri()), server.uri(), "tok".into());
101        list(&mut client, &test_out_json(), None).await.unwrap();
102    }
103
104    #[tokio::test]
105    async fn users_get_returns_not_found_for_unknown_user() {
106        let server = MockServer::start().await;
107        Mock::given(method("GET"))
108            .and(path("/v2/users/nobody@example.com"))
109            .respond_with(ResponseTemplate::new(404).set_body_string("User not found"))
110            .mount(&server)
111            .await;
112
113        let mut client =
114            ZoomClient::new_for_test(format!("{}/v2", server.uri()), server.uri(), "tok".into());
115        let err = get(&mut client, &test_out_json(), "nobody@example.com")
116            .await
117            .unwrap_err();
118        assert!(matches!(err, ApiError::NotFound(_)));
119    }
120
121    #[tokio::test]
122    async fn users_me_delegates_to_get_with_me() {
123        let server = MockServer::start().await;
124        Mock::given(method("GET"))
125            .and(path("/v2/users/me"))
126            .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
127                "id": "self-user", "email": "me@example.com", "display_name": "Me"
128            })))
129            .mount(&server)
130            .await;
131
132        let mut client =
133            ZoomClient::new_for_test(format!("{}/v2", server.uri()), server.uri(), "tok".into());
134        me(&mut client, &test_out_json()).await.unwrap();
135    }
136}