ironflow_cli/commands/
audit_log.rs1use anyhow::Result;
4use chrono::{DateTime, Utc};
5use clap::{Args, Subcommand};
6use ironflow_sdk::IronflowClient;
7use ironflow_sdk::client::ListAuditLogsFilter;
8use ironflow_sdk::types::EventKind;
9use uuid::Uuid;
10
11use crate::commands::parse_enum;
12use crate::output;
13
14#[derive(Debug, Args)]
16pub struct AuditLogArgs {
17 #[command(subcommand)]
19 pub command: AuditLogCommands,
20}
21
22#[derive(Debug, Subcommand)]
24pub enum AuditLogCommands {
25 List {
27 #[arg(long = "run", value_name = "UUID")]
29 run: Option<Uuid>,
30 #[arg(long = "type", value_name = "KIND", value_parser = parse_event_kind)]
32 event_type: Option<EventKind>,
33 #[arg(long)]
35 from: Option<DateTime<Utc>>,
36 #[arg(long)]
38 to: Option<DateTime<Utc>>,
39 #[arg(long)]
41 page: Option<u32>,
42 #[arg(long)]
44 per_page: Option<u32>,
45 },
46}
47
48const ALL_EVENT_KINDS: [EventKind; 21] = [
50 EventKind::RunCreated,
51 EventKind::RunStatusChanged,
52 EventKind::RunFailed,
53 EventKind::RunBudgetExceeded,
54 EventKind::StepCompleted,
55 EventKind::StepFailed,
56 EventKind::ApprovalRequested,
57 EventKind::ApprovalGranted,
58 EventKind::ApprovalRejected,
59 EventKind::ApprovalEscalated,
60 EventKind::LogLine,
61 EventKind::UserSignedIn,
62 EventKind::UserSignedUp,
63 EventKind::UserSignedOut,
64 EventKind::ProviderAccountUpdated,
65 EventKind::SignalAwaited,
66 EventKind::SignalReceived,
67 EventKind::ScheduleOccurrencesMissed,
68 EventKind::WorkflowPaused,
69 EventKind::WorkflowResumed,
70 EventKind::AgentRoleCostCapUpdated,
71];
72
73fn parse_event_kind(raw: &str) -> Result<EventKind, String> {
79 parse_enum(raw, &ALL_EVENT_KINDS, "event type")
80}
81
82pub async fn execute(client: &IronflowClient, args: &AuditLogArgs, json_mode: bool) -> Result<()> {
88 match &args.command {
89 AuditLogCommands::List {
90 run,
91 event_type,
92 from,
93 to,
94 page,
95 per_page,
96 } => {
97 let event_type = event_type.as_ref().map(ToString::to_string);
98 let filter = ListAuditLogsFilter {
99 run_id: *run,
100 event_type: event_type.as_deref(),
101 from: *from,
102 to: *to,
103 page: *page,
104 per_page: *per_page,
105 };
106
107 let response = client.list_audit_logs_filtered(&filter).await?;
108 output::print_output(json_mode, &response, || {
109 output::audit_logs_table(&response.data)
110 })?;
111 }
112 }
113 Ok(())
114}
115
116#[cfg(test)]
117mod tests {
118 use super::*;
119
120 #[test]
121 fn parse_event_kind_accepts_every_declared_kind() {
122 for kind in ALL_EVENT_KINDS {
123 let raw = kind.to_string();
124 assert_eq!(parse_event_kind(&raw).unwrap(), kind);
125 }
126 }
127
128 #[test]
129 fn parse_event_kind_rejects_an_unknown_value_and_lists_the_valid_ones() {
130 let err = parse_event_kind("run_exploded").unwrap_err();
131 assert!(err.contains("unknown event type 'run_exploded'"), "{err}");
132 assert!(err.contains("run_created"), "{err}");
133 }
134}