diff --git a/crates/cli/src/subcommands/list.rs b/crates/cli/src/subcommands/list.rs index d650e98f52d..889b6a93346 100644 --- a/crates/cli/src/subcommands/list.rs +++ b/crates/cli/src/subcommands/list.rs @@ -6,6 +6,7 @@ use crate::util::UNSTABLE_WARNING; use crate::Config; use anyhow::Context; use clap::{ArgMatches, Command}; +use futures::future::join_all; use serde::Deserialize; use spacetimedb_lib::Identity; use tabled::{ @@ -24,12 +25,14 @@ pub fn cli() -> Command { #[derive(Deserialize)] struct DatabasesResult { - pub identities: Vec, + pub identities: Vec, } -#[derive(Tabled, Deserialize)] -#[serde(transparent)] -struct IdentityRow { +#[derive(Tabled)] +struct DatabaseRow { + #[tabled(rename = "Database Name(s)")] + pub db_names: String, + #[tabled(rename = "Identity")] pub db_identity: Identity, } @@ -58,11 +61,12 @@ pub async fn exec(mut config: Config, args: &ArgMatches) -> Result<(), anyhow::E .context("unable to retrieve databases for identity")?; if !result.identities.is_empty() { - let mut table = Table::new(result.identities); + let databases = assemble_rows(&config, server, result.identities).await?; + let mut table = Table::new(databases); table .with(Style::psql()) .with(Modify::new(Columns::first()).with(Alignment::left())); - println!("Associated database identities for {identity}:\n"); + println!("Associated databases for user {identity}:\n"); println!("{table}"); } else { println!("No databases found for {identity}."); @@ -70,3 +74,19 @@ pub async fn exec(mut config: Config, args: &ArgMatches) -> Result<(), anyhow::E Ok(()) } + +async fn assemble_rows( + config: &Config, + server: Option<&str>, + identities: Vec, +) -> anyhow::Result> { + let lookups = identities.into_iter().map(|db_identity| async move { + let response = util::spacetime_reverse_dns(config, &db_identity.to_string(), server).await?; + let db_names: Vec<_> = response.names.into_iter().map(|name| name.to_string()).collect(); + Ok(DatabaseRow { + db_names: db_names.join(", "), + db_identity, + }) + }); + join_all(lookups).await.into_iter().collect::>>() +} diff --git a/crates/smoketests/tests/smoketests/cli/list.rs b/crates/smoketests/tests/smoketests/cli/list.rs new file mode 100644 index 00000000000..54c03144733 --- /dev/null +++ b/crates/smoketests/tests/smoketests/cli/list.rs @@ -0,0 +1,63 @@ +//! CLI list command tests + +use spacetimedb_smoketests::{require_local_server, Smoketest}; +use std::process::Output; + +fn output_stdout(output: &Output) -> String { + String::from_utf8_lossy(&output.stdout).to_string() +} + +fn output_stderr(output: &Output) -> String { + String::from_utf8_lossy(&output.stderr).to_string() +} + +fn assert_success(output: &Output, context: &str) { + assert!( + output.status.success(), + "{context} failed:\nstdout: {}\nstderr: {}", + output_stdout(output), + output_stderr(output), + ); +} + +#[test] +fn cli_list_shows_database_names_and_identities() { + require_local_server!(); + let mut test = Smoketest::builder().autopublish(false).build(); + + let primary_name = format!("list-db-{}", std::process::id()); + let alias_name = format!("{primary_name}-alias"); + let second_alias_name = format!("{primary_name}-alt"); + let identity = test.publish_module_named(&primary_name, false).unwrap(); + + let json_body = format!(r#"["{}","{}"]"#, alias_name, second_alias_name); + let response = test + .api_call_json("PUT", &format!("/v1/database/{primary_name}/names"), &json_body) + .unwrap(); + assert_eq!( + response.status_code, + 200, + "Expected 200 status when replacing names, got {}: {}", + response.status_code, + String::from_utf8_lossy(&response.body) + ); + + let output = test.spacetime_cmd(&["list", "--server", &test.server_url]); + assert_success(&output, "spacetime list"); + + let stdout = output_stdout(&output); + assert!( + stdout.contains("Database Name(s)"), + "missing Database Name(s) column:\n{stdout}" + ); + assert!(stdout.contains("Identity"), "missing Identity column:\n{stdout}"); + assert!(stdout.contains(&alias_name), "missing alias name in output:\n{stdout}"); + assert!( + stdout.contains(&second_alias_name), + "missing second alias name in output:\n{stdout}" + ); + assert!( + stdout.contains(&identity), + "missing database identity in output:\n{stdout}" + ); +} diff --git a/crates/smoketests/tests/smoketests/cli/mod.rs b/crates/smoketests/tests/smoketests/cli/mod.rs index d54c9749851..60ece75d377 100644 --- a/crates/smoketests/tests/smoketests/cli/mod.rs +++ b/crates/smoketests/tests/smoketests/cli/mod.rs @@ -1,5 +1,6 @@ pub mod auth; pub mod dev; pub mod generate; +pub mod list; pub mod publish; pub mod server;