clickup_cli/commands/
audit_log.rs1use crate::client::ClickUpClient;
2use crate::commands::auth::resolve_token;
3use crate::commands::workspace::resolve_workspace;
4use crate::error::CliError;
5use crate::output::OutputConfig;
6use crate::Cli;
7use clap::Subcommand;
8
9#[derive(Subcommand)]
10pub enum AuditLogCommands {
11 Query {
13 #[arg(long)]
15 applicability: String,
16 #[arg(long = "event-type")]
18 event_type: Option<String>,
19 #[arg(long = "event-status")]
21 event_status: Option<String>,
22 #[arg(long = "user-id")]
24 user_id: Vec<String>,
25 #[arg(long = "user-email")]
27 user_email: Vec<String>,
28 #[arg(long)]
30 start_time: Option<i64>,
31 #[arg(long)]
33 end_time: Option<i64>,
34 #[arg(long)]
36 page_rows: Option<i64>,
37 #[arg(long)]
39 page_timestamp: Option<i64>,
40 #[arg(long)]
42 page_direction: Option<String>,
43 },
44}
45
46const AUDIT_LOG_FIELDS: &[&str] = &["id", "eventType", "userId", "createdAt"];
47
48pub async fn execute(command: AuditLogCommands, cli: &Cli) -> Result<(), CliError> {
49 let token = resolve_token(cli)?;
50 let client = ClickUpClient::new(&token, cli.timeout)?;
51 let output = OutputConfig::from_cli(&cli.output, &cli.fields, cli.no_header, cli.quiet);
52
53 match command {
54 AuditLogCommands::Query {
55 applicability,
56 event_type,
57 event_status,
58 user_id,
59 user_email,
60 start_time,
61 end_time,
62 page_rows,
63 page_timestamp,
64 page_direction,
65 } => {
66 let team_id = resolve_workspace(cli)?;
67
68 let mut body = serde_json::json!({ "applicability": applicability });
73
74 let mut filter = serde_json::Map::new();
75 if let Some(t) = event_type {
76 filter.insert("eventType".into(), serde_json::Value::String(t));
77 }
78 if let Some(s) = event_status {
79 filter.insert("eventStatus".into(), serde_json::Value::String(s));
80 }
81 if !user_id.is_empty() {
82 filter.insert(
83 "userId".into(),
84 serde_json::Value::Array(
85 user_id.into_iter().map(serde_json::Value::String).collect(),
86 ),
87 );
88 }
89 if !user_email.is_empty() {
90 filter.insert(
91 "userEmail".into(),
92 serde_json::Value::Array(
93 user_email
94 .into_iter()
95 .map(serde_json::Value::String)
96 .collect(),
97 ),
98 );
99 }
100 if let Some(s) = start_time {
101 filter.insert("startTime".into(), serde_json::Value::Number(s.into()));
102 }
103 if let Some(e) = end_time {
104 filter.insert("endTime".into(), serde_json::Value::Number(e.into()));
105 }
106 if !filter.is_empty() {
107 body["filter"] = serde_json::Value::Object(filter);
108 }
109
110 let mut pagination = serde_json::Map::new();
111 if let Some(n) = page_rows {
112 pagination.insert("pageRows".into(), serde_json::Value::Number(n.into()));
113 }
114 if let Some(t) = page_timestamp {
115 pagination.insert("pageTimestamp".into(), serde_json::Value::Number(t.into()));
116 }
117 if let Some(d) = page_direction {
118 pagination.insert("pageDirection".into(), serde_json::Value::String(d));
119 }
120 if !pagination.is_empty() {
121 body["pagination"] = serde_json::Value::Object(pagination);
122 }
123
124 let resp = client
125 .post(&format!("/v3/workspaces/{}/auditlogs", team_id), &body)
126 .await?;
127
128 if cli.output == "json" {
129 println!("{}", serde_json::to_string_pretty(&resp).unwrap());
130 return Ok(());
131 }
132
133 let logs = resp
134 .get("data")
135 .and_then(|d| d.as_array())
136 .or_else(|| resp.get("audit_logs").and_then(|d| d.as_array()))
137 .cloned()
138 .unwrap_or_default();
139 output.print_items(&logs, AUDIT_LOG_FIELDS, "id");
140 Ok(())
141 }
142 }
143}