Skip to content
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

fix: last cache cli parser bug #25485

Merged
merged 1 commit into from
Oct 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions influxdb3/src/commands/common.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::str::FromStr;

use clap::Parser;
use secrecy::Secret;
use url::Url;
Expand All @@ -21,3 +23,32 @@ pub struct InfluxDb3Config {
#[clap(long = "token", env = "INFLUXDB3_AUTH_TOKEN")]
pub auth_token: Option<Secret<String>>,
}

/// A clap argument privided as a list of items separated by `SEPARATOR`, which by default is a ','
#[derive(Debug, Clone)]
pub struct SeparatedList<T, const SEPARATOR: char = ','>(pub Vec<T>);

impl<T, const SEPARATOR: char> FromStr for SeparatedList<T, SEPARATOR>
where
T: FromStr<Err: Into<anyhow::Error>>,
{
type Err = anyhow::Error;

fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self(
s.split(SEPARATOR)
.map(|s| s.parse::<T>().map_err(Into::into))
.collect::<Result<Vec<T>, Self::Err>>()?,
))
}
}

impl<T, const SEPARATOR: char> IntoIterator for SeparatedList<T, SEPARATOR> {
type Item = T;

type IntoIter = std::vec::IntoIter<Self::Item>;

fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
69 changes: 60 additions & 9 deletions influxdb3/src/commands/last_cache/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,19 @@ use std::error::Error;

use secrecy::ExposeSecret;

use crate::commands::common::InfluxDb3Config;
use crate::commands::common::{InfluxDb3Config, SeparatedList};

#[derive(Debug, clap::Parser)]
pub struct Config {
#[clap(flatten)]
influxdb3_config: InfluxDb3Config,

#[clap(flatten)]
last_cache_config: LastCacheConfig,
}

#[derive(Debug, clap::Parser)]
pub struct LastCacheConfig {
/// The table name for which the cache is being created
#[clap(short = 't', long = "table")]
table: String,
Expand All @@ -19,11 +25,11 @@ pub struct Config {

/// Which columns in the table to use as keys in the cache
#[clap(long = "key-columns")]
key_columns: Option<Vec<String>>,
key_columns: Option<SeparatedList<String>>,

/// Which columns in the table to store as values in the cache
#[clap(long = "value-columns")]
value_columns: Option<Vec<String>>,
value_columns: Option<SeparatedList<String>>,

/// The number of entries per unique key column combination the cache will store
#[clap(long = "count")]
Expand All @@ -40,26 +46,35 @@ pub(super) async fn command(config: Config) -> Result<(), Box<dyn Error>> {
database_name,
auth_token,
} = config.influxdb3_config;
let LastCacheConfig {
table,
cache_name,
key_columns,
value_columns,
count,
ttl,
..
} = config.last_cache_config;
let mut client = influxdb3_client::Client::new(host_url)?;
if let Some(t) = auth_token {
client = client.with_auth_token(t.expose_secret());
}
let mut b = client.api_v3_configure_last_cache_create(database_name, config.table);
let mut b = client.api_v3_configure_last_cache_create(database_name, table);

// Add optional parameters:
if let Some(name) = config.cache_name {
if let Some(name) = cache_name {
b = b.name(name);
}
if let Some(keys) = config.key_columns {
if let Some(keys) = key_columns {
b = b.key_columns(keys);
}
if let Some(vals) = config.value_columns {
if let Some(vals) = value_columns {
b = b.value_columns(vals);
}
if let Some(count) = config.count {
if let Some(count) = count {
b = b.count(count);
}
if let Some(ttl) = config.ttl {
if let Some(ttl) = ttl {
b = b.ttl(ttl);
}

Expand All @@ -74,3 +89,39 @@ pub(super) async fn command(config: Config) -> Result<(), Box<dyn Error>> {

Ok(())
}

#[cfg(test)]
mod tests {
use clap::Parser;

use crate::commands::last_cache::create::LastCacheConfig;

#[test]
fn parse_args() {
let args = LastCacheConfig::parse_from([
"last_cache_create",
"--table",
"foo",
"--cache-name",
"bar",
"--key-columns",
"tag1,tag2,tag3",
"--value-columns",
"field1,field2,field3",
"--ttl",
"3600",
"--count",
"5",
]);
assert_eq!("foo", args.table);
assert!(args.cache_name.is_some_and(|n| n == "bar"));
assert!(args
.key_columns
.is_some_and(|keys| keys.0 == ["tag1", "tag2", "tag3"]));
assert!(args
.value_columns
.is_some_and(|vals| vals.0 == ["field1", "field2", "field3"]));
assert!(args.count.is_some_and(|c| c == 5));
assert!(args.ttl.is_some_and(|t| t == 3600));
}
}