Skip to main content

zoom_cli/commands/
reports.rs

1use crate::api::{ApiError, ZoomClient};
2use crate::output::{self, OutputConfig};
3
4pub async fn meetings(
5    client: &mut ZoomClient,
6    out: &OutputConfig,
7    user: &str,
8    from: &str,
9    to: Option<&str>,
10) -> Result<(), ApiError> {
11    let result = client.list_user_meeting_reports(user, from, to).await?;
12
13    if out.json {
14        out.print_data(&serde_json::to_string_pretty(&result).expect("serialize"));
15    } else {
16        if result.meetings.is_empty() {
17            out.print_message("No meetings found.");
18            return Ok(());
19        }
20        let rows: Vec<Vec<String>> = result
21            .meetings
22            .iter()
23            .map(|m| {
24                vec![
25                    m.id.map(|id| id.to_string()).unwrap_or_default(),
26                    m.topic.clone().unwrap_or_default(),
27                    m.start_time
28                        .as_deref()
29                        .map(output::format_timestamp)
30                        .unwrap_or_else(|| "-".into()),
31                    m.duration
32                        .map(|d| format!("{d} min"))
33                        .unwrap_or_else(|| "-".into()),
34                    m.participants_count
35                        .map(|c| c.to_string())
36                        .unwrap_or_else(|| "-".into()),
37                ]
38            })
39            .collect();
40        out.print_data(&output::table(
41            &["ID", "TOPIC", "START TIME", "DURATION", "PARTICIPANTS"],
42            &rows,
43        ));
44        if let Some(total) = result.total_records {
45            out.print_message(&format!("{total} meeting(s) total"));
46        }
47    }
48    Ok(())
49}
50
51#[cfg(test)]
52mod tests {
53    use super::*;
54    use crate::api::ZoomClient;
55    use wiremock::matchers::{method, path, query_param};
56    use wiremock::{Mock, MockServer, ResponseTemplate};
57
58    fn test_out() -> OutputConfig {
59        OutputConfig {
60            json: true,
61            quiet: true,
62        }
63    }
64
65    #[tokio::test]
66    async fn reports_meetings_empty_is_ok() {
67        let server = MockServer::start().await;
68        Mock::given(method("GET"))
69            .and(path("/v2/report/users/me/meetings"))
70            .and(query_param("from", "2026-04-01"))
71            .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
72                "meetings": [],
73                "total_records": 0,
74                "from": "2026-04-01",
75                "to": "2026-04-30"
76            })))
77            .mount(&server)
78            .await;
79        let mut client =
80            ZoomClient::new_for_test(format!("{}/v2", server.uri()), server.uri(), "tok".into());
81        meetings(&mut client, &test_out(), "me", "2026-04-01", None)
82            .await
83            .unwrap();
84    }
85
86    #[tokio::test]
87    async fn reports_meetings_returns_rows() {
88        let server = MockServer::start().await;
89        Mock::given(method("GET"))
90            .and(path("/v2/report/users/me/meetings"))
91            .and(query_param("from", "2026-04-01"))
92            .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
93                "meetings": [
94                    {
95                        "uuid": "abc==",
96                        "id": 123456789,
97                        "topic": "Weekly Standup",
98                        "start_time": "2026-04-01T09:00:00Z",
99                        "duration": 30,
100                        "participants_count": 8
101                    }
102                ],
103                "total_records": 1,
104                "from": "2026-04-01",
105                "to": "2026-04-30"
106            })))
107            .mount(&server)
108            .await;
109        let mut client =
110            ZoomClient::new_for_test(format!("{}/v2", server.uri()), server.uri(), "tok".into());
111        meetings(&mut client, &test_out(), "me", "2026-04-01", None)
112            .await
113            .unwrap();
114    }
115}