diff --git a/influxdb_iox_client/src/client.rs b/influxdb_iox_client/src/client.rs index a24122a620..5cfdb21654 100644 --- a/influxdb_iox_client/src/client.rs +++ b/influxdb_iox_client/src/client.rs @@ -3,7 +3,8 @@ use std::num::NonZeroU32; use data_types::database_rules::DatabaseRules; use reqwest::{Method, Url}; -use crate::errors::{CreateDatabaseError, Error, ServerErrorResponse}; +use crate::errors::{ClientError, CreateDatabaseError, Error, ServerErrorResponse}; +use data_types::DatabaseName; // TODO: move DatabaseRules / WriterId to the API client @@ -83,12 +84,7 @@ impl Client { name: impl AsRef, rules: &DatabaseRules, ) -> Result<(), CreateDatabaseError> { - const DB_PATH: &str = "iox/api/v1/databases/"; - - let url = self - .url_for(DB_PATH) - .join(name.as_ref()) - .map_err(|_| CreateDatabaseError::InvalidName)?; + let url = self.db_url(name.as_ref())?; let r = self .http @@ -147,6 +143,18 @@ impl Client { .join(path) .expect("failed to construct request URL") } + + fn db_url(&self, database: &str) -> Result { + const DB_PATH: &str = "iox/api/v1/databases/"; + + // Perform validation in the client as URL parser silently drops invalid + // characters + let name = DatabaseName::new(database).map_err(|_| ClientError::InvalidDatabaseName)?; + + self.url_for(DB_PATH) + .join(name.as_ref()) + .map_err(|_| ClientError::InvalidDatabaseName) + } } #[cfg(test)] @@ -202,13 +210,11 @@ mod tests { let endpoint = maybe_skip_integration!(); let c = ClientBuilder::default().build(endpoint).unwrap(); - let rand_name: String = thread_rng() - .sample_iter(&Alphanumeric) - .take(10) - .map(char::from) - .collect(); + c.set_writer_id(NonZeroU32::new(42).unwrap()) + .await + .expect("set ID failed"); - c.create_database(rand_name, &DatabaseRules::default()) + c.create_database(rand_name(), &DatabaseRules::default()) .await .expect("create database failed"); } @@ -218,18 +224,18 @@ mod tests { let endpoint = maybe_skip_integration!(); let c = ClientBuilder::default().build(endpoint).unwrap(); - let rand_name: String = thread_rng() - .sample_iter(&Alphanumeric) - .take(10) - .map(char::from) - .collect(); + c.set_writer_id(NonZeroU32::new(42).unwrap()) + .await + .expect("set ID failed"); - c.create_database(rand_name.clone(), &DatabaseRules::default()) + let db_name = rand_name(); + + c.create_database(db_name.clone(), &DatabaseRules::default()) .await .expect("create database failed"); let err = c - .create_database(rand_name, &DatabaseRules::default()) + .create_database(db_name, &DatabaseRules::default()) .await .expect_err("create database failed"); @@ -241,12 +247,19 @@ mod tests { let endpoint = maybe_skip_integration!(); let c = ClientBuilder::default().build(endpoint).unwrap(); + c.set_writer_id(NonZeroU32::new(42).unwrap()) + .await + .expect("set ID failed"); + let err = c - .create_database("bananas!", &DatabaseRules::default()) + .create_database("my_example\ndb", &DatabaseRules::default()) .await .expect_err("expected request to fail"); - assert!(matches!(dbg!(err), CreateDatabaseError::InvalidName)) + assert!(matches!( + dbg!(err), + CreateDatabaseError::ClientError(ClientError::InvalidDatabaseName) + )); } #[test] @@ -277,4 +290,12 @@ mod tests { c.url_for("/bananas"); } + + fn rand_name() -> String { + thread_rng() + .sample_iter(&Alphanumeric) + .take(10) + .map(char::from) + .collect() + } } diff --git a/influxdb_iox_client/src/errors/client_error.rs b/influxdb_iox_client/src/errors/client_error.rs new file mode 100644 index 0000000000..be9b6ad6fd --- /dev/null +++ b/influxdb_iox_client/src/errors/client_error.rs @@ -0,0 +1,9 @@ +use thiserror::Error; + +/// Error responses when creating a new IOx database. +#[derive(Debug, Error)] +pub enum ClientError { + /// The database name contains an invalid character. + #[error("the database name contains an invalid character")] + InvalidDatabaseName, +} diff --git a/influxdb_iox_client/src/errors/create_database.rs b/influxdb_iox_client/src/errors/create_database.rs index 70971aa45c..495dcd0eee 100644 --- a/influxdb_iox_client/src/errors/create_database.rs +++ b/influxdb_iox_client/src/errors/create_database.rs @@ -1,6 +1,6 @@ use thiserror::Error; -use super::{ApiErrorCode, HttpError, ServerErrorResponse}; +use super::{ApiErrorCode, ClientError, HttpError, ServerErrorResponse}; /// Error responses when creating a new IOx database. #[derive(Debug, Error)] @@ -13,7 +13,7 @@ pub enum CreateDatabaseError { #[error("a database with the requested name already exists")] AlreadyExists, - /// An unknown server error occured. + /// An unknown server error occurred. /// /// The error string contains the error string returned by the server. #[error(transparent)] @@ -22,6 +22,10 @@ pub enum CreateDatabaseError { /// A non-application HTTP request/response error occurred. #[error(transparent)] HttpError(#[from] HttpError), + + /// An error occurred in the client. + #[error(transparent)] + ClientError(#[from] ClientError), } /// Convert a [`ServerErrorResponse`] into a [`CreateDatabaseError`]. diff --git a/influxdb_iox_client/src/errors/mod.rs b/influxdb_iox_client/src/errors/mod.rs index c61615e888..bec8786f0f 100644 --- a/influxdb_iox_client/src/errors/mod.rs +++ b/influxdb_iox_client/src/errors/mod.rs @@ -23,6 +23,9 @@ use thiserror::Error; mod http_error; pub use http_error::*; +mod client_error; +pub use client_error::*; + mod server_error_response; pub use server_error_response::*;