-
Notifications
You must be signed in to change notification settings - Fork 6
Print output in json format when using global --json flag #274
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
338d55f
2c38bb5
e635baa
6d38d33
2d20584
41e1988
c23d785
3fa02bf
c59c3c2
874a426
3d6d42a
3bbe885
3a5ba08
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,6 +12,7 @@ use bytesize::ByteSize; | |
| use clap::ArgAction::SetTrue; | ||
| use clap::{Arg, ArgMatches, Command}; | ||
| use reduct_rs::BucketInfoList; | ||
| use serde::{Deserialize, Serialize}; | ||
| use tabled::settings::Style; | ||
| use tabled::{Table, Tabled}; | ||
|
|
||
|
|
@@ -35,24 +36,35 @@ pub(super) fn ls_bucket_cmd() -> Command { | |
|
|
||
| pub(super) async fn ls_bucket(ctx: &CliContext, args: &ArgMatches) -> anyhow::Result<()> { | ||
| let alias_or_url = args.get_one::<String>("ALIAS_OR_URL").unwrap(); | ||
| let is_json = ctx.json().unwrap_or(false); | ||
|
|
||
| let client = build_client(ctx, alias_or_url).await?; | ||
|
|
||
| let bucket_list = client.bucket_list().await?; | ||
| if args.get_flag("full") { | ||
| print_full_list(ctx, bucket_list); | ||
| print_full_list(ctx, bucket_list, is_json); | ||
| } else { | ||
| print_list(ctx, bucket_list); | ||
| print_list(ctx, bucket_list, is_json); | ||
| } | ||
| Ok(()) | ||
| } | ||
|
|
||
| fn print_list(ctx: &CliContext, bucket_list: BucketInfoList) { | ||
| fn print_list(ctx: &CliContext, bucket_list: BucketInfoList, is_json: bool) { | ||
| if is_json { | ||
| let buckets = bucket_list | ||
| .buckets | ||
| .iter() | ||
| .map(|bucket| bucket.name.as_str()) | ||
| .collect::<Vec<_>>(); | ||
| output!(ctx, "{}", serde_json::to_string_pretty(&buckets).unwrap()); | ||
| return; | ||
| } | ||
| for bucket in bucket_list.buckets { | ||
| output!(ctx, "{}", bucket.name); | ||
| } | ||
| } | ||
|
|
||
| #[derive(Tabled)] | ||
| #[derive(Deserialize, Serialize, Tabled)] | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ideally, I would like to serialize the JSON data we receive from the backend in order to decouple the API and its table representation. |
||
| struct BucketRow { | ||
| #[tabled(rename = "Name")] | ||
| name: String, | ||
|
|
@@ -85,8 +97,11 @@ fn record_range_values(oldest: u64, latest: u64, is_empty: bool) -> (String, Str | |
| (oldest_value, latest_value) | ||
| } | ||
|
|
||
| fn print_full_list(ctx: &CliContext, bucket_list: BucketInfoList) { | ||
| fn print_full_list(ctx: &CliContext, bucket_list: BucketInfoList, is_json: bool) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have the flag in the context already. |
||
| if bucket_list.buckets.is_empty() { | ||
| if is_json { | ||
| output!(ctx, "{}", "[]"); | ||
| } | ||
| return; | ||
| } | ||
|
|
||
|
|
@@ -115,14 +130,23 @@ fn print_full_list(ctx: &CliContext, bucket_list: BucketInfoList) { | |
| }) | ||
| .collect::<Vec<_>>(); | ||
|
|
||
| if is_json { | ||
| output!(ctx, "{}", serde_json::to_string_pretty(&rows).unwrap()); | ||
| return; | ||
| } | ||
| let table = Table::new(rows).with(Style::markdown()).to_string(); | ||
| output!(ctx, "{}", table); | ||
| } | ||
|
|
||
| #[cfg(test)] | ||
| mod tests { | ||
|
|
||
| use super::*; | ||
| use crate::context::tests::{bucket, bucket2, context}; | ||
| use crate::context::{ | ||
| tests::{bucket, bucket2, context, MockOutput}, | ||
| ContextBuilder, | ||
| }; | ||
| use reduct_rs::{Bucket, ReductClient}; | ||
| use rstest::rstest; | ||
|
|
||
| #[rstest] | ||
|
|
@@ -210,4 +234,132 @@ mod tests { | |
| ] | ||
| ); | ||
| } | ||
|
|
||
| #[rstest] | ||
| #[tokio::test] | ||
| async fn test_ls_bucket_full_json_with_buckets( | ||
| context: CliContext, | ||
| #[future] bucket: String, | ||
| #[future] bucket2: String, | ||
| ) { | ||
| let bucket = bucket.await; | ||
| let bucket2 = bucket2.await; | ||
|
|
||
| let ctx = ContextBuilder::new() | ||
| .config_path(context.config_path()) | ||
| .json(Some(true)) | ||
| .output(Box::new(MockOutput::new())) | ||
| .build(); | ||
|
|
||
| let args = ls_bucket_cmd().get_matches_from(vec!["ls", "local", "--full"]); | ||
| let client = build_client(&ctx, "local").await.unwrap(); | ||
|
|
||
| // Create buckets | ||
| let _ = create_test_bucket_with_entry(&bucket, &client).await; | ||
| let _ = create_test_bucket_with_entry(&bucket2, &client).await; | ||
|
|
||
| // List buckets | ||
| ls_bucket(&ctx, &args).await.unwrap(); | ||
|
|
||
| let rows: Vec<BucketRow> = serde_json::from_str(&ctx.stdout().history()[0]).unwrap(); | ||
|
|
||
| assert!(rows.iter().any(|row| row.name == bucket)); | ||
| assert!(rows.iter().any(|row| row.name == bucket2)); | ||
| } | ||
|
|
||
| #[rstest] | ||
| #[tokio::test] | ||
| async fn test_ls_bucket_full_json_with_no_bucket( | ||
| context: CliContext, | ||
| #[future] bucket: String, | ||
| #[future] bucket2: String, | ||
| ) { | ||
| let bucket = bucket.await; | ||
| let bucket2 = bucket2.await; | ||
|
|
||
| let ctx = ContextBuilder::new() | ||
| .config_path(context.config_path()) | ||
| .json(Some(true)) | ||
| .output(Box::new(MockOutput::new())) | ||
| .build(); | ||
|
|
||
| let args = ls_bucket_cmd().get_matches_from(vec!["ls", "local", "--full"]); | ||
|
|
||
| // List buckets | ||
| ls_bucket(&ctx, &args).await.unwrap(); | ||
|
|
||
| let rows: Vec<BucketRow> = serde_json::from_str(&ctx.stdout().history()[0]).unwrap(); | ||
|
|
||
| assert_eq!(rows.iter().any(|row| row.name == bucket), false); | ||
| assert_eq!(rows.iter().any(|row| row.name == bucket2), false); | ||
| } | ||
|
|
||
| #[rstest] | ||
| #[tokio::test] | ||
| async fn test_ls_bucket_json_with_buckets( | ||
| context: CliContext, | ||
| #[future] bucket: String, | ||
| #[future] bucket2: String, | ||
| ) { | ||
| let ctx = ContextBuilder::new() | ||
| .config_path(context.config_path()) | ||
| .json(Some(true)) | ||
| .output(Box::new(MockOutput::new())) | ||
| .build(); | ||
|
|
||
| let args = ls_bucket_cmd().get_matches_from(vec!["ls", "local"]); | ||
| let client = build_client(&ctx, "local").await.unwrap(); | ||
|
|
||
| // Create buckets | ||
| let bucket = create_test_bucket_with_entry(&bucket.await, &client).await; | ||
| let bucket2 = create_test_bucket_with_entry(&bucket2.await, &client).await; | ||
|
|
||
| // List buckets | ||
| ls_bucket(&ctx, &args).await.unwrap(); | ||
|
|
||
| let rows: Vec<String> = serde_json::from_str(&ctx.stdout().history()[0]).unwrap(); | ||
|
|
||
| assert!(rows.contains(&bucket.name().to_string())); | ||
| assert!(rows.contains(&bucket2.name().to_string())); | ||
| } | ||
|
|
||
| #[rstest] | ||
| #[tokio::test] | ||
| async fn test_ls_bucket_json_with_no_bucket( | ||
| context: CliContext, | ||
| #[future] bucket: String, | ||
| #[future] bucket2: String, | ||
| ) { | ||
| let bucket = bucket.await; | ||
| let bucket2 = bucket2.await; | ||
|
|
||
| let ctx = ContextBuilder::new() | ||
| .config_path(context.config_path()) | ||
| .json(Some(true)) | ||
| .output(Box::new(MockOutput::new())) | ||
| .build(); | ||
|
|
||
| let args = ls_bucket_cmd().get_matches_from(vec!["ls", "local"]); | ||
|
|
||
| // List buckets | ||
| ls_bucket(&ctx, &args).await.unwrap(); | ||
|
|
||
| // We have $system bucket to consider too so len is 1. | ||
| let rows: Vec<String> = serde_json::from_str(&ctx.stdout().history()[0]).unwrap(); | ||
|
|
||
| assert_eq!(rows.contains(&bucket), false); | ||
| assert_eq!(rows.contains(&bucket2), false); | ||
| } | ||
|
|
||
| async fn create_test_bucket_with_entry(bucket_name: &str, client: &ReductClient) -> Bucket { | ||
| let bucket = client.create_bucket(bucket_name).send().await.unwrap(); | ||
| bucket | ||
| .write_record("test") | ||
| .data("data") | ||
| .timestamp_us(0) | ||
| .send() | ||
| .await | ||
| .unwrap(); | ||
| bucket | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have the JSON flag in the context, so there is no need to add an additional parameter.