Skip to content

Commit c709eac

Browse files
authored
Add backup download command (#8)
* Add backup scope * Add backup download command
1 parent b27954b commit c709eac

4 files changed

Lines changed: 180 additions & 2 deletions

File tree

src/cli.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -996,6 +996,31 @@ pub enum BackupCommands {
996996
#[arg(long)]
997997
description: Option<String>,
998998
},
999+
/// Download a backup archive
1000+
Download {
1001+
#[command(subcommand)]
1002+
command: BackupDownloadCommands,
1003+
},
1004+
}
1005+
1006+
#[derive(Subcommand)]
1007+
pub enum BackupDownloadCommands {
1008+
/// Create a backup download request
1009+
Create {
1010+
/// Site ID
1011+
site_id: String,
1012+
/// Backup ID
1013+
backup_id: String,
1014+
},
1015+
/// Check backup download status
1016+
Status {
1017+
/// Site ID
1018+
site_id: String,
1019+
/// Backup ID
1020+
backup_id: String,
1021+
/// Download ID
1022+
download_id: String,
1023+
},
9991024
}
10001025

10011026
#[derive(Subcommand)]

src/commands/backup.rs

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,3 +178,94 @@ pub fn create(
178178

179179
Ok(())
180180
}
181+
182+
pub fn download_create(
183+
client: &ApiClient,
184+
site_id: &str,
185+
backup_id: &str,
186+
format: OutputFormat,
187+
) -> Result<(), ApiError> {
188+
let response: Value = client.post_empty(&format!(
189+
"/api/v1/vector/sites/{}/backups/{}/downloads",
190+
site_id, backup_id
191+
))?;
192+
193+
if format == OutputFormat::Json {
194+
print_json(&response);
195+
return Ok(());
196+
}
197+
198+
let data = &response["data"];
199+
print_message(&format!(
200+
"Download requested: {} ({})",
201+
data["id"].as_str().unwrap_or("-"),
202+
data["status"].as_str().unwrap_or("-")
203+
));
204+
print_message("\nCheck status with:");
205+
print_message(&format!(
206+
" vector backup download status {} {} {}",
207+
site_id,
208+
backup_id,
209+
data["id"].as_str().unwrap_or("DOWNLOAD_ID")
210+
));
211+
212+
Ok(())
213+
}
214+
215+
pub fn download_status(
216+
client: &ApiClient,
217+
site_id: &str,
218+
backup_id: &str,
219+
download_id: &str,
220+
format: OutputFormat,
221+
) -> Result<(), ApiError> {
222+
let response: Value = client.get(&format!(
223+
"/api/v1/vector/sites/{}/backups/{}/downloads/{}",
224+
site_id, backup_id, download_id
225+
))?;
226+
227+
if format == OutputFormat::Json {
228+
print_json(&response);
229+
return Ok(());
230+
}
231+
232+
let data = &response["data"];
233+
print_key_value(vec![
234+
("ID", data["id"].as_str().unwrap_or("-").to_string()),
235+
("Status", data["status"].as_str().unwrap_or("-").to_string()),
236+
(
237+
"Size (bytes)",
238+
format_option(&data["size_bytes"].as_u64().map(|v| v.to_string())),
239+
),
240+
(
241+
"Duration (ms)",
242+
format_option(&data["duration_ms"].as_u64().map(|v| v.to_string())),
243+
),
244+
(
245+
"Error",
246+
format_option(&data["error_message"].as_str().map(String::from)),
247+
),
248+
(
249+
"Download URL",
250+
format_option(&data["download_url"].as_str().map(String::from)),
251+
),
252+
(
253+
"Download Expires",
254+
format_option(&data["download_expires_at"].as_str().map(String::from)),
255+
),
256+
(
257+
"Started At",
258+
format_option(&data["started_at"].as_str().map(String::from)),
259+
),
260+
(
261+
"Completed At",
262+
format_option(&data["completed_at"].as_str().map(String::from)),
263+
),
264+
(
265+
"Created At",
266+
format_option(&data["created_at"].as_str().map(String::from)),
267+
),
268+
]);
269+
270+
Ok(())
271+
}

src/main.rs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ use std::process;
1111
use api::{ApiClient, ApiError, EXIT_SUCCESS};
1212
use cli::{
1313
AccountApiKeyCommands, AccountCommands, AccountSecretCommands, AccountSshKeyCommands,
14-
AuthCommands, BackupCommands, Cli, Commands, DbCommands, DbExportCommands,
15-
DbImportSessionCommands, DeployCommands, EnvCommands, EnvDbCommands,
14+
AuthCommands, BackupCommands, BackupDownloadCommands, Cli, Commands, DbCommands,
15+
DbExportCommands, DbImportSessionCommands, DeployCommands, EnvCommands, EnvDbCommands,
1616
EnvDbImportSessionCommands, EnvSecretCommands, EventCommands, McpCommands, RestoreCommands,
1717
SiteCommands, SiteSshKeyCommands, SslCommands, WafAllowedReferrerCommands,
1818
WafBlockedIpCommands, WafBlockedReferrerCommands, WafCommands, WafRateLimitCommands,
@@ -634,6 +634,24 @@ fn run_backup(command: BackupCommands, format: OutputFormat) -> Result<(), ApiEr
634634
scope,
635635
description,
636636
} => backup::create(&client, &site_id, &scope, description, format),
637+
BackupCommands::Download { command } => run_backup_download(&client, command, format),
638+
}
639+
}
640+
641+
fn run_backup_download(
642+
client: &ApiClient,
643+
command: BackupDownloadCommands,
644+
format: OutputFormat,
645+
) -> Result<(), ApiError> {
646+
match command {
647+
BackupDownloadCommands::Create { site_id, backup_id } => {
648+
backup::download_create(client, &site_id, &backup_id, format)
649+
}
650+
BackupDownloadCommands::Status {
651+
site_id,
652+
backup_id,
653+
download_id,
654+
} => backup::download_status(client, &site_id, &backup_id, &download_id, format),
637655
}
638656
}
639657

tests/cli.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,50 @@ fn test_backup_help() {
176176
assert!(stdout.contains("list"));
177177
assert!(stdout.contains("show"));
178178
assert!(stdout.contains("create"));
179+
assert!(stdout.contains("download"));
180+
}
181+
182+
#[test]
183+
fn test_backup_download_help() {
184+
let output = vector_cmd()
185+
.args(["backup", "download", "--help"])
186+
.output()
187+
.expect("Failed to run");
188+
assert!(output.status.success());
189+
let stdout = String::from_utf8_lossy(&output.stdout);
190+
assert!(stdout.contains("create"));
191+
assert!(stdout.contains("status"));
192+
}
193+
194+
#[test]
195+
fn test_backup_download_create_requires_auth() {
196+
let output = vector_cmd()
197+
.args(["backup", "download", "create", "test-site", "test-backup"])
198+
.env("VECTOR_CONFIG_DIR", &nonexistent_config_dir())
199+
.env_remove("VECTOR_API_KEY")
200+
.output()
201+
.expect("Failed to run");
202+
assert!(!output.status.success());
203+
assert_eq!(output.status.code(), Some(2)); // EXIT_AUTH_ERROR
204+
}
205+
206+
#[test]
207+
fn test_backup_download_status_requires_auth() {
208+
let output = vector_cmd()
209+
.args([
210+
"backup",
211+
"download",
212+
"status",
213+
"test-site",
214+
"test-backup",
215+
"test-download",
216+
])
217+
.env("VECTOR_CONFIG_DIR", &nonexistent_config_dir())
218+
.env_remove("VECTOR_API_KEY")
219+
.output()
220+
.expect("Failed to run");
221+
assert!(!output.status.success());
222+
assert_eq!(output.status.code(), Some(2)); // EXIT_AUTH_ERROR
179223
}
180224

181225
#[test]

0 commit comments

Comments
 (0)