diff --git a/.travis.yml b/.travis.yml index 768874a..04078e3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -52,7 +52,7 @@ matrix: wget https://github.com/SimonKagstrom/kcov/archive/master.tar.gz && tar xzf master.tar.gz && mkdir kcov-master/build && cd kcov-master/build && cmake .. && make && sudo make install && cd ../.. && - kcov --coveralls-id=$TRAVIS_JOB_ID --exclude-pattern=/.cargo target/kcov target/debug/influxdb-* + kcov --coveralls-id=$TRAVIS_JOB_ID --exclude-pattern=/.cargo --exclude-path=./tests target/kcov target/debug/influxdb-* addons: apt: packages: diff --git a/Cargo.lock b/Cargo.lock index 43bf3c3..c2dcebc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -411,6 +411,7 @@ dependencies = [ "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", "tokio 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index e5ae342..aa9c92b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ description = "InfluxDB Driver for Rust" keywords = ["influxdb", "database", "influx"] license-file = "LICENSE.md" readme = "README.md" -include = ["src/**/*", "tests/**/*", "Cargo.toml", "LICENSE"] +include = ["src/**/*", "tests/**/*", "Cargo.toml", "LICENSE.md"] repository = "https://github.com/Empty2k12/influxdb-rust" [badges] @@ -20,6 +20,7 @@ futures = "0.1.27" tokio = "0.1.20" itertools = "0.8" failure = "0.1.5" +url = "1.7.2" serde = { version = "1.0.92", optional = true } serde_json = { version = "1.0", optional = true } diff --git a/src/client/mod.rs b/src/client/mod.rs index a72c547..5aabf82 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -23,6 +23,8 @@ use std::mem; use crate::error::InfluxDbError; use crate::query::{InfluxDbQuery, QueryType}; +use url::form_urlencoded; + /// Internal Representation of a Client pub struct InfluxDbClient { url: String, @@ -134,11 +136,14 @@ impl InfluxDbClient { let client = match q_type { QueryType::ReadQuery => { let read_query = query.get(); + let encoded: String = form_urlencoded::Serializer::new(String::new()) + .append_pair("db", self.database_name()) + .append_pair("q", &read_query) + .finish(); let http_query_string = format!( - "{url}/query?db={db}&q={read_query}", + "{url}/query?{encoded}", url = self.database_url(), - db = self.database_name(), - read_query = read_query, + encoded = encoded ); if read_query.contains("SELECT") || read_query.contains("SHOW") { diff --git a/src/integrations/serde_integration.rs b/src/integrations/serde_integration.rs index 54fa8e6..6254fa3 100644 --- a/src/integrations/serde_integration.rs +++ b/src/integrations/serde_integration.rs @@ -25,7 +25,7 @@ //! let mut rt = tokio::runtime::current_thread::Runtime::new().unwrap(); //! let client = InfluxDbClient::new("http://localhost:8086", "test"); //! let query = InfluxDbQuery::raw_read_query("SELECT temperature FROM /weather_[a-z]*$/ WHERE time > now() - 1m ORDER BY DESC"); -//! let _result = rt.block_on(client.json_query::(query)) +//! let _result = rt.block_on(client.json_query::(query)) //! .map(|it| { //! it.map(|series_vec| { //! series_vec @@ -36,6 +36,7 @@ //! }).collect::>() //! }) //! }); +//! ``` use crate::client::InfluxDbClient; @@ -49,7 +50,11 @@ use serde::Deserialize; use serde_json; use crate::error::InfluxDbError; -use crate::query::{InfluxDbQuery, QueryType}; + +use crate::query::read_query::InfluxDbReadQuery; +use crate::query::InfluxDbQuery; + +use url::form_urlencoded; #[derive(Deserialize)] #[doc(hidden)] @@ -77,55 +82,41 @@ pub struct InfluxDbSeries { } impl InfluxDbClient { - pub fn json_query( + pub fn json_query( &self, - q: Q, + q: InfluxDbReadQuery, ) -> Box>>, Error = InfluxDbError>> where - Q: InfluxDbQuery, T: DeserializeOwned, { use futures::future; - let q_type = q.get_type(); - let query = match q.build() { - Err(err) => { + let query = q.build().unwrap(); + + let client = { + let read_query = query.get(); + let encoded: String = form_urlencoded::Serializer::new(String::new()) + .append_pair("db", self.database_name()) + .append_pair("q", &read_query) + .finish(); + let http_query_string = format!( + "{url}/query?{encoded}", + url = self.database_url(), + encoded = encoded + ); + + if read_query.contains("SELECT") || read_query.contains("SHOW") { + Client::new().get(http_query_string.as_str()) + } else { let error = InfluxDbError::InvalidQueryError { - error: format!("{}", err), + error: String::from( + "Only SELECT and SHOW queries supported with JSON deserialization", + ), }; return Box::new( future::err::>>, InfluxDbError>(error), ); } - Ok(query) => query, - }; - - let client = match q_type { - QueryType::ReadQuery => { - let read_query = query.get(); - let http_query_string = format!( - "{url}/query?db={db}&q={read_query}", - url = self.database_url(), - db = self.database_name(), - read_query = read_query, - ); - - if read_query.contains("SELECT") || read_query.contains("SHOW") { - Client::new().get(http_query_string.as_str()) - } else { - Client::new().post(http_query_string.as_str()) - } - } - QueryType::WriteQuery => Client::new() - .post( - format!( - "{url}/write?db={db}", - url = self.database_url(), - db = self.database_name(), - ) - .as_str(), - ) - .body(query.get()), }; Box::new( diff --git a/src/query/mod.rs b/src/query/mod.rs index 2d8288e..6f9116a 100644 --- a/src/query/mod.rs +++ b/src/query/mod.rs @@ -89,6 +89,14 @@ impl ValidQuery { self.0 } } +impl From for ValidQuery +where + T: ToString, +{ + fn from(string: T) -> Self { + Self(string.to_string()) + } +} impl PartialEq for ValidQuery { fn eq(&self, other: &String) -> bool { &self.0 == other @@ -105,3 +113,21 @@ pub enum QueryType { ReadQuery, WriteQuery, } + +#[cfg(test)] +mod tests { + use super::ValidQuery; + + #[test] + fn test_equality_str() { + assert_eq!(ValidQuery::from("hello"), "hello"); + } + + #[test] + fn test_equality_string() { + assert_eq!( + ValidQuery::from(String::from("hello")), + String::from("hello") + ); + } +} diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index bbf72a0..cec8542 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -90,10 +90,56 @@ fn test_json_query() { let client = create_client(); let query = InfluxDbQuery::raw_read_query("SELECT * FROM weather"); - let result = get_runtime().block_on(client.json_query::(query)); + let result = get_runtime().block_on(client.json_query::(query)); assert!( result.is_ok(), format!("We couldn't read from the DB: {}", result.unwrap_err()) ); } + +#[test] +#[cfg(feature = "use-serde")] +/// INTEGRATION TEST +/// +/// This integration test tests whether using the wrong query method fails building the query +fn test_serde_query_build_error() { + use serde::Deserialize; + + #[derive(Deserialize, Debug)] + struct Weather { + time: String, + temperature: i32, + } + + let client = create_client(); + let query = InfluxDbQuery::raw_read_query("CREATE database should_fail"); + let result = get_runtime().block_on(client.json_query::(query)); + + assert!( + result.is_err(), + format!( + "Should not be able to build JSON query that is not SELECT or SELECT .. INTO: {}", + result.unwrap_err() + ) + ); +} + +#[test] +#[cfg(feature = "use-serde")] +/// INTEGRATION TEST +/// +/// This test case tests whether JSON can be decoded from a InfluxDB response +fn test_raw_query_build_error() { + let client = create_client(); + let query = InfluxDbQuery::write_query("weather").add_tag("season", "summer"); + let result = get_runtime().block_on(client.query(query)); + + assert!( + result.is_err(), + format!( + "Should not be able to build JSON query that is not SELECT or SELECT .. INTO: {}", + result.unwrap_err() + ) + ); +}