From 9db0b021fbf1fcc6b71cfed5322e4599874ac735 Mon Sep 17 00:00:00 2001 From: Josh Priddle Date: Fri, 27 Feb 2026 11:13:31 -0500 Subject: [PATCH 1/2] Update for route flattening --- src/cli.rs | 45 +++++++++++++-------- src/commands/backup.rs | 86 ++++++++++++++++++++++++++++------------- src/commands/restore.rs | 71 ++++++++++++++++++++++++---------- src/main.rs | 36 ++++++++--------- src/output.rs | 15 +++++++ 5 files changed, 171 insertions(+), 82 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index 6ba944b..3bc3e9d 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -967,10 +967,17 @@ pub enum EventCommands { #[derive(Subcommand)] pub enum BackupCommands { - /// List backups for a site + /// List backups List { - /// Site ID - site_id: String, + /// Filter by site ID + #[arg(long)] + site_id: Option, + /// Filter by environment ID + #[arg(long)] + environment_id: Option, + /// Filter by type (site, environment) + #[arg(long = "type")] + backup_type: Option, /// Page number #[arg(long, default_value = "1")] page: u32, @@ -980,15 +987,17 @@ pub enum BackupCommands { }, /// Show backup details Show { - /// Site ID - site_id: String, /// Backup ID backup_id: String, }, /// Create a manual backup Create { /// Site ID - site_id: String, + #[arg(long)] + site_id: Option, + /// Environment ID + #[arg(long)] + environment_id: Option, /// Backup scope (full, database, files) #[arg(long, default_value = "full")] scope: String, @@ -1007,15 +1016,11 @@ pub enum BackupCommands { pub enum BackupDownloadCommands { /// Create a backup download request Create { - /// Site ID - site_id: String, /// Backup ID backup_id: String, }, /// Check backup download status Status { - /// Site ID - site_id: String, /// Backup ID backup_id: String, /// Download ID @@ -1025,10 +1030,20 @@ pub enum BackupDownloadCommands { #[derive(Subcommand)] pub enum RestoreCommands { - /// List restores for a site + /// List restores List { - /// Site ID - site_id: String, + /// Filter by site ID + #[arg(long)] + site_id: Option, + /// Filter by environment ID + #[arg(long)] + environment_id: Option, + /// Filter by type (site, environment) + #[arg(long = "type")] + restore_type: Option, + /// Filter by backup ID + #[arg(long)] + backup_id: Option, /// Page number #[arg(long, default_value = "1")] page: u32, @@ -1038,15 +1053,11 @@ pub enum RestoreCommands { }, /// Show restore details Show { - /// Site ID - site_id: String, /// Restore ID restore_id: String, }, /// Create a restore from a backup Create { - /// Site ID - site_id: String, /// Backup ID to restore from backup_id: String, /// Restore scope (full, database, files) diff --git a/src/commands/backup.rs b/src/commands/backup.rs index f7274bd..6d0cea6 100644 --- a/src/commands/backup.rs +++ b/src/commands/backup.rs @@ -3,19 +3,28 @@ use serde_json::Value; use crate::api::{ApiClient, ApiError}; use crate::output::{ - OutputFormat, extract_pagination, format_option, print_json, print_key_value, print_message, - print_pagination, print_table, + OutputFormat, extract_pagination, format_archivable_type, format_option, print_json, + print_key_value, print_message, print_pagination, print_table, }; #[derive(Debug, Serialize)] -struct PaginationQuery { +struct ListBackupsQuery { + #[serde(skip_serializing_if = "Option::is_none")] + r#type: Option, + #[serde(skip_serializing_if = "Option::is_none")] + site_id: Option, + #[serde(skip_serializing_if = "Option::is_none")] + environment_id: Option, page: u32, per_page: u32, } #[derive(Debug, Serialize)] struct CreateBackupRequest { - r#type: String, + #[serde(skip_serializing_if = "Option::is_none")] + site_id: Option, + #[serde(skip_serializing_if = "Option::is_none")] + environment_id: Option, scope: String, #[serde(skip_serializing_if = "Option::is_none")] description: Option, @@ -23,14 +32,21 @@ struct CreateBackupRequest { pub fn list( client: &ApiClient, - site_id: &str, + site_id: Option, + environment_id: Option, + backup_type: Option, page: u32, per_page: u32, format: OutputFormat, ) -> Result<(), ApiError> { - let query = PaginationQuery { page, per_page }; - let response: Value = - client.get_with_query(&format!("/api/v1/vector/sites/{}/backups", site_id), &query)?; + let query = ListBackupsQuery { + r#type: backup_type, + site_id, + environment_id, + page, + per_page, + }; + let response: Value = client.get_with_query("/api/v1/vector/backups", &query)?; if format == OutputFormat::Json { print_json(&response); @@ -51,6 +67,10 @@ pub fn list( .map(|b| { vec![ b["id"].as_str().unwrap_or("-").to_string(), + b["archivable_type"] + .as_str() + .map(format_archivable_type) + .unwrap_or_else(|| "-".to_string()), b["type"].as_str().unwrap_or("-").to_string(), b["scope"].as_str().unwrap_or("-").to_string(), b["status"].as_str().unwrap_or("-").to_string(), @@ -61,7 +81,7 @@ pub fn list( .collect(); print_table( - vec!["ID", "Type", "Scope", "Status", "Description", "Created"], + vec!["ID", "Model", "Type", "Scope", "Status", "Description", "Created"], rows, ); @@ -74,14 +94,10 @@ pub fn list( pub fn show( client: &ApiClient, - site_id: &str, backup_id: &str, format: OutputFormat, ) -> Result<(), ApiError> { - let response: Value = client.get(&format!( - "/api/v1/vector/sites/{}/backups/{}", - site_id, backup_id - ))?; + let response: Value = client.get(&format!("/api/v1/vector/backups/{}", backup_id))?; if format == OutputFormat::Json { print_json(&response); @@ -92,6 +108,20 @@ pub fn show( print_key_value(vec![ ("ID", backup["id"].as_str().unwrap_or("-").to_string()), + ( + "Model", + backup["archivable_type"] + .as_str() + .map(format_archivable_type) + .unwrap_or_else(|| "-".to_string()), + ), + ( + "Model ID", + backup["archivable_id"] + .as_str() + .unwrap_or("-") + .to_string(), + ), ("Type", backup["type"].as_str().unwrap_or("-").to_string()), ("Scope", backup["scope"].as_str().unwrap_or("-").to_string()), ( @@ -133,19 +163,26 @@ pub fn show( pub fn create( client: &ApiClient, - site_id: &str, + site_id: Option, + environment_id: Option, scope: &str, description: Option, format: OutputFormat, ) -> Result<(), ApiError> { + if site_id.is_none() && environment_id.is_none() { + return Err(ApiError::Other( + "Either --site-id or --environment-id is required".to_string(), + )); + } + let body = CreateBackupRequest { - r#type: "manual".to_string(), + site_id, + environment_id, scope: scope.to_string(), description, }; - let response: Value = - client.post(&format!("/api/v1/vector/sites/{}/backups", site_id), &body)?; + let response: Value = client.post("/api/v1/vector/backups", &body)?; if format == OutputFormat::Json { print_json(&response); @@ -181,13 +218,12 @@ pub fn create( pub fn download_create( client: &ApiClient, - site_id: &str, backup_id: &str, format: OutputFormat, ) -> Result<(), ApiError> { let response: Value = client.post_empty(&format!( - "/api/v1/vector/sites/{}/backups/{}/downloads", - site_id, backup_id + "/api/v1/vector/backups/{}/downloads", + backup_id ))?; if format == OutputFormat::Json { @@ -203,8 +239,7 @@ pub fn download_create( )); print_message("\nCheck status with:"); print_message(&format!( - " vector backup download status {} {} {}", - site_id, + " vector backup download status {} {}", backup_id, data["id"].as_str().unwrap_or("DOWNLOAD_ID") )); @@ -214,14 +249,13 @@ pub fn download_create( pub fn download_status( client: &ApiClient, - site_id: &str, backup_id: &str, download_id: &str, format: OutputFormat, ) -> Result<(), ApiError> { let response: Value = client.get(&format!( - "/api/v1/vector/sites/{}/backups/{}/downloads/{}", - site_id, backup_id, download_id + "/api/v1/vector/backups/{}/downloads/{}", + backup_id, download_id ))?; if format == OutputFormat::Json { diff --git a/src/commands/restore.rs b/src/commands/restore.rs index 4e86f0c..41253e4 100644 --- a/src/commands/restore.rs +++ b/src/commands/restore.rs @@ -3,12 +3,20 @@ use serde_json::Value; use crate::api::{ApiClient, ApiError}; use crate::output::{ - OutputFormat, extract_pagination, format_option, print_json, print_key_value, print_message, - print_pagination, print_table, + OutputFormat, extract_pagination, format_archivable_type, format_option, print_json, + print_key_value, print_message, print_pagination, print_table, }; #[derive(Debug, Serialize)] -struct PaginationQuery { +struct ListRestoresQuery { + #[serde(skip_serializing_if = "Option::is_none")] + r#type: Option, + #[serde(skip_serializing_if = "Option::is_none")] + site_id: Option, + #[serde(skip_serializing_if = "Option::is_none")] + environment_id: Option, + #[serde(skip_serializing_if = "Option::is_none")] + backup_id: Option, page: u32, per_page: u32, } @@ -21,16 +29,23 @@ struct CreateRestoreRequest { pub fn list( client: &ApiClient, - site_id: &str, + site_id: Option, + environment_id: Option, + restore_type: Option, + backup_id: Option, page: u32, per_page: u32, format: OutputFormat, ) -> Result<(), ApiError> { - let query = PaginationQuery { page, per_page }; - let response: Value = client.get_with_query( - &format!("/api/v1/vector/sites/{}/restores", site_id), - &query, - )?; + let query = ListRestoresQuery { + r#type: restore_type, + site_id, + environment_id, + backup_id, + page, + per_page, + }; + let response: Value = client.get_with_query("/api/v1/vector/restores", &query)?; if format == OutputFormat::Json { print_json(&response); @@ -51,6 +66,10 @@ pub fn list( .map(|r| { vec![ r["id"].as_str().unwrap_or("-").to_string(), + r["archivable_type"] + .as_str() + .map(format_archivable_type) + .unwrap_or_else(|| "-".to_string()), r["vector_backup_id"].as_str().unwrap_or("-").to_string(), r["scope"].as_str().unwrap_or("-").to_string(), r["status"].as_str().unwrap_or("-").to_string(), @@ -59,7 +78,10 @@ pub fn list( }) .collect(); - print_table(vec!["ID", "Backup ID", "Scope", "Status", "Created"], rows); + print_table( + vec!["ID", "Model", "Backup ID", "Scope", "Status", "Created"], + rows, + ); if let Some((current, last, total)) = extract_pagination(&response) { print_pagination(current, last, total); @@ -70,14 +92,11 @@ pub fn list( pub fn show( client: &ApiClient, - site_id: &str, restore_id: &str, format: OutputFormat, ) -> Result<(), ApiError> { - let response: Value = client.get(&format!( - "/api/v1/vector/sites/{}/restores/{}", - site_id, restore_id - ))?; + let response: Value = + client.get(&format!("/api/v1/vector/restores/{}", restore_id))?; if format == OutputFormat::Json { print_json(&response); @@ -88,6 +107,20 @@ pub fn show( print_key_value(vec![ ("ID", restore["id"].as_str().unwrap_or("-").to_string()), + ( + "Model", + restore["archivable_type"] + .as_str() + .map(format_archivable_type) + .unwrap_or_else(|| "-".to_string()), + ), + ( + "Model ID", + restore["archivable_id"] + .as_str() + .unwrap_or("-") + .to_string(), + ), ( "Backup ID", restore["vector_backup_id"] @@ -138,7 +171,6 @@ pub fn show( pub fn create( client: &ApiClient, - site_id: &str, backup_id: &str, scope: &str, format: OutputFormat, @@ -148,8 +180,7 @@ pub fn create( scope: scope.to_string(), }; - let response: Value = - client.post(&format!("/api/v1/vector/sites/{}/restores", site_id), &body)?; + let response: Value = client.post("/api/v1/vector/restores", &body)?; if format == OutputFormat::Json { print_json(&response); @@ -160,8 +191,8 @@ pub fn create( let restore_id = restore["id"].as_str().unwrap_or("-"); print_message(&format!( - "Restore initiated. Use `vector restore show {} {}` to check progress.", - site_id, restore_id + "Restore initiated. Use `vector restore show {}` to check progress.", + restore_id )); print_key_value(vec![ diff --git a/src/main.rs b/src/main.rs index 36670e9..212eb56 100644 --- a/src/main.rs +++ b/src/main.rs @@ -623,17 +623,18 @@ fn run_backup(command: BackupCommands, format: OutputFormat) -> Result<(), ApiEr match command { BackupCommands::List { site_id, + environment_id, + backup_type, page, per_page, - } => backup::list(&client, &site_id, page, per_page, format), - BackupCommands::Show { site_id, backup_id } => { - backup::show(&client, &site_id, &backup_id, format) - } + } => backup::list(&client, site_id, environment_id, backup_type, page, per_page, format), + BackupCommands::Show { backup_id } => backup::show(&client, &backup_id, format), BackupCommands::Create { site_id, + environment_id, scope, description, - } => backup::create(&client, &site_id, &scope, description, format), + } => backup::create(&client, site_id, environment_id, &scope, description, format), BackupCommands::Download { command } => run_backup_download(&client, command, format), } } @@ -644,14 +645,13 @@ fn run_backup_download( format: OutputFormat, ) -> Result<(), ApiError> { match command { - BackupDownloadCommands::Create { site_id, backup_id } => { - backup::download_create(client, &site_id, &backup_id, format) + BackupDownloadCommands::Create { backup_id } => { + backup::download_create(client, &backup_id, format) } BackupDownloadCommands::Status { - site_id, backup_id, download_id, - } => backup::download_status(client, &site_id, &backup_id, &download_id, format), + } => backup::download_status(client, &backup_id, &download_id, format), } } @@ -661,18 +661,16 @@ fn run_restore(command: RestoreCommands, format: OutputFormat) -> Result<(), Api match command { RestoreCommands::List { site_id, + environment_id, + restore_type, + backup_id, page, per_page, - } => restore::list(&client, &site_id, page, per_page, format), - RestoreCommands::Show { - site_id, - restore_id, - } => restore::show(&client, &site_id, &restore_id, format), - RestoreCommands::Create { - site_id, - backup_id, - scope, - } => restore::create(&client, &site_id, &backup_id, &scope, format), + } => restore::list(&client, site_id, environment_id, restore_type, backup_id, page, per_page, format), + RestoreCommands::Show { restore_id } => restore::show(&client, &restore_id, format), + RestoreCommands::Create { backup_id, scope } => { + restore::create(&client, &backup_id, &scope, format) + } } } diff --git a/src/output.rs b/src/output.rs index b730956..a0cb629 100644 --- a/src/output.rs +++ b/src/output.rs @@ -67,6 +67,14 @@ pub fn format_option(opt: &Option) -> String { } } +pub fn format_archivable_type(value: &str) -> String { + match value { + "vector_site" => "Site".to_string(), + "vector_environment" => "Environment".to_string(), + _ => value.to_string(), + } +} + pub fn format_bool(b: bool) -> String { if b { "Yes".to_string() @@ -116,6 +124,13 @@ mod tests { assert_eq!(format_option::(&None), "-"); } + #[test] + fn test_format_archivable_type() { + assert_eq!(format_archivable_type("vector_site"), "Site"); + assert_eq!(format_archivable_type("vector_environment"), "Environment"); + assert_eq!(format_archivable_type("unknown"), "unknown"); + } + #[test] fn test_format_bool() { assert_eq!(format_bool(true), "Yes"); From af6627e044ce1b749d2be3d8851655f42adcef0f Mon Sep 17 00:00:00 2001 From: Josh Priddle Date: Fri, 27 Feb 2026 11:27:10 -0500 Subject: [PATCH 2/2] lint --- src/commands/backup.rs | 27 +++++++++++++-------------- src/commands/restore.rs | 14 +++----------- src/main.rs | 30 +++++++++++++++++++++++++++--- 3 files changed, 43 insertions(+), 28 deletions(-) diff --git a/src/commands/backup.rs b/src/commands/backup.rs index 6d0cea6..545b48d 100644 --- a/src/commands/backup.rs +++ b/src/commands/backup.rs @@ -81,7 +81,15 @@ pub fn list( .collect(); print_table( - vec!["ID", "Model", "Type", "Scope", "Status", "Description", "Created"], + vec![ + "ID", + "Model", + "Type", + "Scope", + "Status", + "Description", + "Created", + ], rows, ); @@ -92,11 +100,7 @@ pub fn list( Ok(()) } -pub fn show( - client: &ApiClient, - backup_id: &str, - format: OutputFormat, -) -> Result<(), ApiError> { +pub fn show(client: &ApiClient, backup_id: &str, format: OutputFormat) -> Result<(), ApiError> { let response: Value = client.get(&format!("/api/v1/vector/backups/{}", backup_id))?; if format == OutputFormat::Json { @@ -117,10 +121,7 @@ pub fn show( ), ( "Model ID", - backup["archivable_id"] - .as_str() - .unwrap_or("-") - .to_string(), + backup["archivable_id"].as_str().unwrap_or("-").to_string(), ), ("Type", backup["type"].as_str().unwrap_or("-").to_string()), ("Scope", backup["scope"].as_str().unwrap_or("-").to_string()), @@ -221,10 +222,8 @@ pub fn download_create( backup_id: &str, format: OutputFormat, ) -> Result<(), ApiError> { - let response: Value = client.post_empty(&format!( - "/api/v1/vector/backups/{}/downloads", - backup_id - ))?; + let response: Value = + client.post_empty(&format!("/api/v1/vector/backups/{}/downloads", backup_id))?; if format == OutputFormat::Json { print_json(&response); diff --git a/src/commands/restore.rs b/src/commands/restore.rs index 41253e4..14f2d88 100644 --- a/src/commands/restore.rs +++ b/src/commands/restore.rs @@ -90,13 +90,8 @@ pub fn list( Ok(()) } -pub fn show( - client: &ApiClient, - restore_id: &str, - format: OutputFormat, -) -> Result<(), ApiError> { - let response: Value = - client.get(&format!("/api/v1/vector/restores/{}", restore_id))?; +pub fn show(client: &ApiClient, restore_id: &str, format: OutputFormat) -> Result<(), ApiError> { + let response: Value = client.get(&format!("/api/v1/vector/restores/{}", restore_id))?; if format == OutputFormat::Json { print_json(&response); @@ -116,10 +111,7 @@ pub fn show( ), ( "Model ID", - restore["archivable_id"] - .as_str() - .unwrap_or("-") - .to_string(), + restore["archivable_id"].as_str().unwrap_or("-").to_string(), ), ( "Backup ID", diff --git a/src/main.rs b/src/main.rs index 212eb56..622cbb8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -627,14 +627,29 @@ fn run_backup(command: BackupCommands, format: OutputFormat) -> Result<(), ApiEr backup_type, page, per_page, - } => backup::list(&client, site_id, environment_id, backup_type, page, per_page, format), + } => backup::list( + &client, + site_id, + environment_id, + backup_type, + page, + per_page, + format, + ), BackupCommands::Show { backup_id } => backup::show(&client, &backup_id, format), BackupCommands::Create { site_id, environment_id, scope, description, - } => backup::create(&client, site_id, environment_id, &scope, description, format), + } => backup::create( + &client, + site_id, + environment_id, + &scope, + description, + format, + ), BackupCommands::Download { command } => run_backup_download(&client, command, format), } } @@ -666,7 +681,16 @@ fn run_restore(command: RestoreCommands, format: OutputFormat) -> Result<(), Api backup_id, page, per_page, - } => restore::list(&client, site_id, environment_id, restore_type, backup_id, page, per_page, format), + } => restore::list( + &client, + site_id, + environment_id, + restore_type, + backup_id, + page, + per_page, + format, + ), RestoreCommands::Show { restore_id } => restore::show(&client, &restore_id, format), RestoreCommands::Create { backup_id, scope } => { restore::create(&client, &backup_id, &scope, format)