zoom_cli/commands/
reports.rs1use 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}