diff --git a/influxdb/src/client/mod.rs b/influxdb/src/client/mod.rs index ed9e5dc8..84a6491d 100644 --- a/influxdb/src/client/mod.rs +++ b/influxdb/src/client/mod.rs @@ -25,13 +25,22 @@ use crate::query::QueryType; use crate::Error; use crate::Query; +#[derive(Clone, Debug, PartialEq, Eq)] +#[non_exhaustive] +pub enum InfluxDbVersion { + V1, + V2, + V3, +} + #[derive(Clone)] /// Internal Representation of a Client -pub struct Client { +pub struct Client { pub(crate) url: Arc, pub(crate) parameters: Arc>, pub(crate) token: Option, - pub(crate) client: HttpClient, + pub(crate) client: H, + pub(crate) version: InfluxDbVersion, } struct RedactPassword<'a>(&'a HashMap<&'static str, String>); @@ -50,17 +59,18 @@ impl<'a> Debug for RedactPassword<'a> { } } -impl Debug for Client { +impl Debug for Client { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("Client") .field("url", &self.url) .field("parameters", &RedactPassword(&self.parameters)) + .field("version", &self.version) .finish_non_exhaustive() } } -impl Client { - /// Instantiates a new [`Client`](crate::Client) +impl Client { + /// Instantiates a new [`Client`](crate::Client) for InfluxDB v1. /// /// # Arguments /// @@ -82,11 +92,78 @@ impl Client { { let mut parameters = HashMap::<&str, String>::new(); parameters.insert("db", database.into()); - Client { + Self { url: Arc::new(url.into()), parameters: Arc::new(parameters), client: HttpClient::new(), token: None, + version: InfluxDbVersion::V1, + } + } + + /// Instantiates a new [`Client`](crate::Client) for InfluxDB v2. + /// + /// # Arguments + /// + /// * `url`: The URL where InfluxDB is running (ex. `http://localhost:8086`). + /// * `token`: The InfluxDB v2 authentication token. + /// * `bucket`: The InfluxDB v2 bucket. + /// + /// # Examples + /// + /// ```rust + /// use influxdb::Client; + /// + /// let _client = Client::v2("http://localhost:8086", "some-token", "my-bucket"); + /// ``` + #[must_use = "Creating a client is pointless unless you use it"] + pub fn v2(url: S1, token: S2, bucket: S3) -> Self + where + S1: Into, + S2: Into, + S3: Into, + { + let mut parameters = HashMap::<&str, String>::new(); + parameters.insert("bucket", bucket.into()); + Self { + url: Arc::new(url.into()), + parameters: Arc::new(parameters), + client: HttpClient::new(), + token: Some(token.into()), + version: InfluxDbVersion::V2, + } + } + + /// Instantiates a new [`Client`](crate::Client) for InfluxDB v3. + /// + /// # Arguments + /// + /// * `url`: The URL where InfluxDB is running (ex. `http://localhost:8086`). + /// * `token`: The InfluxDB v3 authentication token. + /// * `database`: The InfluxDB v3 database. + /// + /// # Examples + /// + /// ```rust + /// use influxdb::Client; + /// + /// let _client = Client::v3("http://localhost:8086", "some-token", "my-database"); + /// ``` + #[must_use = "Creating a client is pointless unless you use it"] + pub fn v3(url: S1, token: S2, database: S3) -> Self + where + S1: Into, + S2: Into, + S3: Into, + { + let mut parameters = HashMap::<&str, String>::new(); + parameters.insert("db", database.into()); + Self { + url: Arc::new(url.into()), + parameters: Arc::new(parameters), + client: HttpClient::new(), + token: Some(token.into()), + version: InfluxDbVersion::V3, } } @@ -289,7 +366,7 @@ pub(crate) fn check_status(res: &HttpResponse) -> Result<(), Error> { #[cfg(test)] mod tests { - use super::Client; + use super::{Client, InfluxDbVersion}; use indoc::indoc; #[test] @@ -304,6 +381,7 @@ mod tests { "p": "", "u": "user", }, + version: V1, .. } "# }; @@ -335,4 +413,17 @@ mod tests { assert_eq!(with_auth.parameters.get("db").unwrap(), "database"); assert_eq!(with_auth.token.unwrap(), "token"); } + + #[test] + fn test_v2_and_v3_clients() { + let v2_client = Client::v2("http://localhost:8086", "token", "bucket"); + assert_eq!(v2_client.version, InfluxDbVersion::V2); + assert_eq!(v2_client.token.unwrap(), "token"); + assert_eq!(v2_client.parameters.get("bucket").unwrap(), "bucket"); + + let v3_client = Client::v3("http://localhost:8086", "token", "database"); + assert_eq!(v3_client.version, InfluxDbVersion::V3); + assert_eq!(v3_client.token.unwrap(), "token"); + assert_eq!(v3_client.parameters.get("db").unwrap(), "database"); + } } diff --git a/influxdb/src/integrations/serde_integration/mod.rs b/influxdb/src/integrations/serde_integration/mod.rs index e4000be2..315364cb 100644 --- a/influxdb/src/integrations/serde_integration/mod.rs +++ b/influxdb/src/integrations/serde_integration/mod.rs @@ -120,7 +120,7 @@ pub struct TaggedSeries { pub values: Vec, } -impl Client { +impl Client { pub async fn json_query(&self, q: ReadQuery) -> Result { let query = q.build().map_err(|err| Error::InvalidQueryError { error: err.to_string(), diff --git a/influxdb/src/lib.rs b/influxdb/src/lib.rs index 4661fd5c..ea8c0dbe 100644 --- a/influxdb/src/lib.rs +++ b/influxdb/src/lib.rs @@ -121,7 +121,7 @@ mod client; mod error; mod query; -pub use client::Client; +pub use client::{Client, InfluxVersion1, InfluxVersion2, InfluxVersion3}; pub use error::Error; pub use query::{ read_query::ReadQuery, diff --git a/influxdb/tests/derive_integration_tests.rs b/influxdb/tests/derive_integration_tests.rs index 751899a6..348a8c22 100644 --- a/influxdb/tests/derive_integration_tests.rs +++ b/influxdb/tests/derive_integration_tests.rs @@ -10,7 +10,7 @@ use influxdb::{Query, ReadQuery, Timestamp}; #[cfg(feature = "serde")] use serde_derive::Deserialize; -use utilities::{assert_result_ok, create_client, create_db, delete_db, run_test}; +use utilities::{assert_result_ok, run_test}; #[derive(Debug, PartialEq)] #[cfg_attr(feature = "derive", derive(InfluxDbWriteable))] diff --git a/influxdb/tests/integration_tests.rs b/influxdb/tests/integration_tests_v1.rs similarity index 89% rename from influxdb/tests/integration_tests.rs rename to influxdb/tests/integration_tests_v1.rs index 2261c720..a9421568 100644 --- a/influxdb/tests/integration_tests.rs +++ b/influxdb/tests/integration_tests_v1.rs @@ -5,7 +5,7 @@ mod utilities; use serde_derive::Deserialize; use utilities::{ - assert_result_err, assert_result_ok, create_client, create_db, delete_db, run_test, + assert_result_err, assert_result_ok, create_client_v1, create_db_v1, delete_db_v1, run_test, }; use influxdb::InfluxDbWriteable; @@ -17,7 +17,7 @@ use influxdb::{Client, Error, ReadQuery, Timestamp}; #[tokio::test] #[cfg(not(any(tarpaulin_include)))] async fn test_ping_influx_db_tokio() { - let client = create_client("notusedhere"); + let client = create_client_v1("notusedhere"); let result = client.ping().await; assert_result_ok(&result); @@ -34,8 +34,10 @@ async fn test_ping_influx_db_tokio() { #[tokio::test] #[cfg(not(tarpaulin_include))] async fn test_connection_error() { + + let test_name = "test_connection_error"; - let client = + let client: Client = Client::new("http://127.0.0.1:10086", test_name).with_auth("nopriv_user", "password"); let read_query = ReadQuery::new("SELECT * FROM weather"); let read_result = client.query(read_query).await; @@ -59,7 +61,9 @@ async fn test_authed_write_and_read() { run_test( || async move { - let client = + + + let client: Client = Client::new("http://127.0.0.1:9086", TEST_NAME).with_auth("admin", "password"); let query = format!("CREATE DATABASE {TEST_NAME}"); client @@ -67,7 +71,7 @@ async fn test_authed_write_and_read() { .await .expect("could not setup db"); - let client = + let client: Client = Client::new("http://127.0.0.1:9086", TEST_NAME).with_auth("admin", "password"); let write_query = Timestamp::Hours(11) .into_query("weather") @@ -84,7 +88,9 @@ async fn test_authed_write_and_read() { ); }, || async move { - let client = + + + let client: Client = Client::new("http://127.0.0.1:9086", TEST_NAME).with_auth("admin", "password"); let query = format!("DROP DATABASE {TEST_NAME}"); @@ -109,7 +115,9 @@ async fn test_wrong_authed_write_and_read() { run_test( || async move { - let client = + + + let client: Client = Client::new("http://127.0.0.1:9086", TEST_NAME).with_auth("admin", "password"); let query = format!("CREATE DATABASE {TEST_NAME}"); client @@ -117,7 +125,7 @@ async fn test_wrong_authed_write_and_read() { .await .expect("could not setup db"); - let client = + let client: Client = Client::new("http://127.0.0.1:9086", TEST_NAME).with_auth("wrong_user", "password"); let write_query = Timestamp::Hours(11) .into_query("weather") @@ -143,7 +151,7 @@ async fn test_wrong_authed_write_and_read() { ), } - let client = Client::new("http://127.0.0.1:9086", TEST_NAME) + let client: Client = Client::new("http://127.0.0.1:9086", TEST_NAME) .with_auth("nopriv_user", "password"); let read_query = ReadQuery::new("SELECT * FROM weather"); let read_result = client.query(read_query).await; @@ -157,7 +165,9 @@ async fn test_wrong_authed_write_and_read() { } }, || async move { - let client = + + + let client: Client = Client::new("http://127.0.0.1:9086", TEST_NAME).with_auth("admin", "password"); let query = format!("DROP DATABASE {TEST_NAME}"); client @@ -181,14 +191,17 @@ async fn test_non_authed_write_and_read() { run_test( || async move { - let client = + + + let client: Client = Client::new("http://127.0.0.1:9086", TEST_NAME).with_auth("admin", "password"); let query = format!("CREATE DATABASE {TEST_NAME}"); client .query(ReadQuery::new(query)) .await .expect("could not setup db"); - let non_authed_client = Client::new("http://127.0.0.1:9086", TEST_NAME); + let non_authed_client: Client = + Client::new("http://127.0.0.1:9086", TEST_NAME); let write_query = Timestamp::Hours(11) .into_query("weather") .add_field("temperature", 82); @@ -215,7 +228,9 @@ async fn test_non_authed_write_and_read() { } }, || async move { - let client = + + + let client: Client = Client::new("http://127.0.0.1:9086", TEST_NAME).with_auth("admin", "password"); let query = format!("DROP DATABASE {TEST_NAME}"); client @@ -237,8 +252,8 @@ async fn test_write_and_read_field() { run_test( || async move { - create_db(TEST_NAME).await.expect("could not setup db"); - let client = create_client(TEST_NAME); + create_db_v1(TEST_NAME).await.expect("could not setup db"); + let client = create_client_v1(TEST_NAME); let write_query = Timestamp::Hours(11) .into_query("weather") .add_field("temperature", 82); @@ -254,7 +269,9 @@ async fn test_write_and_read_field() { ); }, || async move { - delete_db(TEST_NAME).await.expect("could not clean up db"); + delete_db_v1(TEST_NAME) + .await + .expect("could not clean up db"); }, ) .await; @@ -273,14 +290,17 @@ async fn test_json_non_authed_read() { run_test( || async move { - let client = + + + let client: Client = Client::new("http://127.0.0.1:9086", TEST_NAME).with_auth("admin", "password"); let query = format!("CREATE DATABASE {TEST_NAME}"); client .query(ReadQuery::new(query)) .await .expect("could not setup db"); - let non_authed_client = Client::new("http://127.0.0.1:9086", TEST_NAME); + let non_authed_client: Client = + Client::new("http://127.0.0.1:9086", TEST_NAME); let read_query = ReadQuery::new("SELECT * FROM weather"); let read_result = non_authed_client.json_query(read_query).await; @@ -294,7 +314,9 @@ async fn test_json_non_authed_read() { } }, || async move { - let client = + + + let client: Client = Client::new("http://127.0.0.1:9086", TEST_NAME).with_auth("admin", "password"); let query = format!("DROP DATABASE {TEST_NAME}"); @@ -318,7 +340,9 @@ async fn test_json_authed_read() { run_test( || async move { - let client = + + + let client: Client = Client::new("http://127.0.0.1:9086", TEST_NAME).with_auth("admin", "password"); let query = format!("CREATE DATABASE {TEST_NAME}"); client @@ -331,7 +355,9 @@ async fn test_json_authed_read() { assert_result_ok(&read_result); }, || async move { - let client = + + + let client: Client = Client::new("http://127.0.0.1:9086", TEST_NAME).with_auth("admin", "password"); let query = format!("DROP DATABASE {TEST_NAME}"); @@ -356,9 +382,9 @@ async fn test_write_and_read_option() { run_test( || { async move { - create_db(TEST_NAME).await.expect("could not setup db"); + create_db_v1(TEST_NAME).await.expect("could not setup db"); - let client = create_client(TEST_NAME); + let client = create_client_v1(TEST_NAME); // Todo: Convert this to derive based insert for easier comparison of structs let write_query = Timestamp::Hours(11) .into_query("weather") @@ -394,7 +420,7 @@ async fn test_write_and_read_option() { } }, || async move { - delete_db("test_write_and_read_option") + delete_db_v1("test_write_and_read_option") .await .expect("could not clean up db"); }, @@ -414,9 +440,9 @@ async fn test_json_query() { run_test( || async move { - create_db(TEST_NAME).await.expect("could not setup db"); + create_db_v1(TEST_NAME).await.expect("could not setup db"); - let client = create_client(TEST_NAME); + let client = create_client_v1(TEST_NAME); let write_query = Timestamp::Hours(11) .into_query("weather") @@ -446,7 +472,9 @@ async fn test_json_query() { ); }, || async move { - delete_db(TEST_NAME).await.expect("could not clean up db"); + delete_db_v1(TEST_NAME) + .await + .expect("could not clean up db"); }, ) .await; @@ -464,9 +492,9 @@ async fn test_json_query_tagged() { run_test( || async move { - create_db(TEST_NAME).await.expect("could not setup db"); + create_db_v1(TEST_NAME).await.expect("could not setup db"); - let client = create_client(TEST_NAME); + let client = create_client_v1(TEST_NAME); let write_query = Timestamp::Hours(11) .into_query("weather") @@ -508,7 +536,9 @@ async fn test_json_query_tagged() { ); }, || async move { - delete_db(TEST_NAME).await.expect("could not clean up db"); + delete_db_v1(TEST_NAME) + .await + .expect("could not clean up db"); }, ) .await; @@ -526,9 +556,9 @@ async fn test_json_query_vec() { run_test( || async move { - create_db(TEST_NAME).await.expect("could not setup db"); + create_db_v1(TEST_NAME).await.expect("could not setup db"); - let client = create_client(TEST_NAME); + let client = create_client_v1(TEST_NAME); let write_query1 = Timestamp::Hours(11) .into_query("temperature_vec") .add_field("temperature", 16); @@ -558,7 +588,9 @@ async fn test_json_query_vec() { assert_eq!(result.unwrap().series[0].values.len(), 3); }, || async move { - delete_db(TEST_NAME).await.expect("could not clean up db"); + delete_db_v1(TEST_NAME) + .await + .expect("could not clean up db"); }, ) .await; @@ -575,7 +607,7 @@ async fn test_serde_multi_query() { run_test( || async move { - create_db(TEST_NAME).await.expect("could not setup db"); + create_db_v1(TEST_NAME).await.expect("could not setup db"); #[derive(Deserialize, Debug, PartialEq)] struct Temperature { @@ -589,7 +621,7 @@ async fn test_serde_multi_query() { humidity: i32, } - let client = create_client(TEST_NAME); + let client = create_client_v1(TEST_NAME); let write_query = Timestamp::Hours(11) .into_query("temperature") .add_field("temperature", 16); @@ -632,7 +664,9 @@ async fn test_serde_multi_query() { ); }, || async move { - delete_db(TEST_NAME).await.expect("could not clean up db"); + delete_db_v1(TEST_NAME) + .await + .expect("could not clean up db"); }, ) .await; @@ -645,7 +679,7 @@ async fn test_serde_multi_query() { #[cfg(feature = "serde")] #[cfg(not(tarpaulin_include))] async fn test_wrong_query_errors() { - let client = create_client("test_name"); + let client = create_client_v1("test_name"); let result = client .json_query(ReadQuery::new("CREATE DATABASE this_should_fail")) .await; diff --git a/influxdb/tests/integration_tests_v2.rs b/influxdb/tests/integration_tests_v2.rs index 303a4b6c..20b2605d 100644 --- a/influxdb/tests/integration_tests_v2.rs +++ b/influxdb/tests/integration_tests_v2.rs @@ -15,7 +15,9 @@ use influxdb::{Client, Error, ReadQuery, Timestamp}; async fn test_authed_write_and_read() { run_test( || async move { - let client = Client::new("http://127.0.0.1:2086", "mydb").with_token("admintoken"); + + + let client = Client::v2("http://127.0.0.1:2086", "admintoken", "mydb"); let write_query = Timestamp::Hours(11) .into_query("weather") .add_field("temperature", 82); @@ -31,7 +33,9 @@ async fn test_authed_write_and_read() { ); }, || async move { - let client = Client::new("http://127.0.0.1:2086", "mydb").with_token("admintoken"); + + + let client = Client::v2("http://127.0.0.1:2086", "admintoken", "mydb"); let read_query = ReadQuery::new("DROP MEASUREMENT \"weather\""); let read_result = client.query(read_query).await; assert_result_ok(&read_result); @@ -51,7 +55,9 @@ async fn test_wrong_authed_write_and_read() { run_test( || async move { - let client = Client::new("http://127.0.0.1:2086", "mydb").with_token("falsetoken"); + + + let client = Client::v2("http://127.0.0.1:2086", "falsetoken", "mydb"); let write_query = Timestamp::Hours(11) .into_query("weather") .add_field("temperature", 82); @@ -91,7 +97,9 @@ async fn test_non_authed_write_and_read() { run_test( || async move { - let non_authed_client = Client::new("http://127.0.0.1:2086", "mydb"); + + + let non_authed_client = Client::v2("http://127.0.0.1:2086", "", "mydb"); let write_query = Timestamp::Hours(11) .into_query("weather") .add_field("temperature", 82); diff --git a/influxdb/tests/utilities.rs b/influxdb/tests/utilities.rs index dc3b17b3..918bf878 100644 --- a/influxdb/tests/utilities.rs +++ b/influxdb/tests/utilities.rs @@ -1,4 +1,5 @@ use futures_util::FutureExt; + use influxdb::{Client, Error, ReadQuery}; use std::future::Future; use std::panic::{AssertUnwindSafe, UnwindSafe}; @@ -16,7 +17,7 @@ pub fn assert_result_ok(result: &Result< #[allow(dead_code)] #[cfg(not(tarpaulin_include))] -pub fn create_client(db_name: T) -> Client +pub fn create_client_v1(db_name: T) -> Client where T: Into, { @@ -25,24 +26,55 @@ where #[allow(dead_code)] #[cfg(not(tarpaulin_include))] -pub async fn create_db(name: T) -> Result +pub fn create_client_v2(bucket: T) -> Client +where + T: Into, +{ + Client::v2("http://127.0.0.1:8086", "", bucket) +} + +#[allow(dead_code)] +#[cfg(not(tarpaulin_include))] +pub async fn create_db_v1(name: T) -> Result +where + T: Into, +{ + let test_name = name.into(); + let query = format!("CREATE DATABASE {test_name}"); + create_client_v1(test_name).query(ReadQuery::new(query)).await +} + +#[allow(dead_code)] +#[cfg(not(tarpaulin_include))] +pub async fn create_db_v2(name: T) -> Result where T: Into, { let test_name = name.into(); let query = format!("CREATE DATABASE {test_name}"); - create_client(test_name).query(ReadQuery::new(query)).await + create_client_v2(test_name).query(ReadQuery::new(query)).await +} + +#[allow(dead_code)] +#[cfg(not(tarpaulin_include))] +pub async fn delete_db_v1(name: T) -> Result +where + T: Into, +{ + let test_name = name.into(); + let query = format!("DROP DATABASE {test_name}"); + create_client_v1(test_name).query(ReadQuery::new(query)).await } #[allow(dead_code)] #[cfg(not(tarpaulin_include))] -pub async fn delete_db(name: T) -> Result +pub async fn delete_db_v2(name: T) -> Result where T: Into, { let test_name = name.into(); let query = format!("DROP DATABASE {test_name}"); - create_client(test_name).query(ReadQuery::new(query)).await + create_client_v2(test_name).query(ReadQuery::new(query)).await } #[cfg(not(tarpaulin_include))]