Skip to content

Commit 70a2bfc

Browse files
authored
URLEncode Query & Improve Test Coverage (#5)
* Start improving test coverage * Update .travis.yml * More tests * exclude tests from coverage * URL Escape Database Queries * Remove debug output
1 parent b514595 commit 70a2bfc

File tree

7 files changed

+114
-44
lines changed

7 files changed

+114
-44
lines changed

Diff for: .travis.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ matrix:
5252
wget https://github.com/SimonKagstrom/kcov/archive/master.tar.gz &&
5353
tar xzf master.tar.gz && mkdir kcov-master/build && cd kcov-master/build && cmake .. && make &&
5454
sudo make install && cd ../.. &&
55-
kcov --coveralls-id=$TRAVIS_JOB_ID --exclude-pattern=/.cargo target/kcov target/debug/influxdb-*
55+
kcov --coveralls-id=$TRAVIS_JOB_ID --exclude-pattern=/.cargo --exclude-path=./tests target/kcov target/debug/influxdb-*
5656
addons:
5757
apt:
5858
packages:

Diff for: Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: Cargo.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ description = "InfluxDB Driver for Rust"
77
keywords = ["influxdb", "database", "influx"]
88
license-file = "LICENSE.md"
99
readme = "README.md"
10-
include = ["src/**/*", "tests/**/*", "Cargo.toml", "LICENSE"]
10+
include = ["src/**/*", "tests/**/*", "Cargo.toml", "LICENSE.md"]
1111
repository = "https://github.com/Empty2k12/influxdb-rust"
1212

1313
[badges]
@@ -20,6 +20,7 @@ futures = "0.1.27"
2020
tokio = "0.1.20"
2121
itertools = "0.8"
2222
failure = "0.1.5"
23+
url = "1.7.2"
2324
serde = { version = "1.0.92", optional = true }
2425
serde_json = { version = "1.0", optional = true }
2526

Diff for: src/client/mod.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ use std::mem;
2323
use crate::error::InfluxDbError;
2424
use crate::query::{InfluxDbQuery, QueryType};
2525

26+
use url::form_urlencoded;
27+
2628
/// Internal Representation of a Client
2729
pub struct InfluxDbClient {
2830
url: String,
@@ -134,11 +136,14 @@ impl InfluxDbClient {
134136
let client = match q_type {
135137
QueryType::ReadQuery => {
136138
let read_query = query.get();
139+
let encoded: String = form_urlencoded::Serializer::new(String::new())
140+
.append_pair("db", self.database_name())
141+
.append_pair("q", &read_query)
142+
.finish();
137143
let http_query_string = format!(
138-
"{url}/query?db={db}&q={read_query}",
144+
"{url}/query?{encoded}",
139145
url = self.database_url(),
140-
db = self.database_name(),
141-
read_query = read_query,
146+
encoded = encoded
142147
);
143148

144149
if read_query.contains("SELECT") || read_query.contains("SHOW") {

Diff for: src/integrations/serde_integration.rs

+29-38
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
//! let mut rt = tokio::runtime::current_thread::Runtime::new().unwrap();
2626
//! let client = InfluxDbClient::new("http://localhost:8086", "test");
2727
//! let query = InfluxDbQuery::raw_read_query("SELECT temperature FROM /weather_[a-z]*$/ WHERE time > now() - 1m ORDER BY DESC");
28-
//! let _result = rt.block_on(client.json_query::<WeatherWithoutCityName, _>(query))
28+
//! let _result = rt.block_on(client.json_query::<WeatherWithoutCityName>(query))
2929
//! .map(|it| {
3030
//! it.map(|series_vec| {
3131
//! series_vec
@@ -36,6 +36,7 @@
3636
//! }).collect::<Vec<Weather>>()
3737
//! })
3838
//! });
39+
//! ```
3940
4041
use crate::client::InfluxDbClient;
4142

@@ -49,7 +50,11 @@ use serde::Deserialize;
4950
use serde_json;
5051

5152
use crate::error::InfluxDbError;
52-
use crate::query::{InfluxDbQuery, QueryType};
53+
54+
use crate::query::read_query::InfluxDbReadQuery;
55+
use crate::query::InfluxDbQuery;
56+
57+
use url::form_urlencoded;
5358

5459
#[derive(Deserialize)]
5560
#[doc(hidden)]
@@ -77,55 +82,41 @@ pub struct InfluxDbSeries<T> {
7782
}
7883

7984
impl InfluxDbClient {
80-
pub fn json_query<T: 'static, Q>(
85+
pub fn json_query<T: 'static>(
8186
&self,
82-
q: Q,
87+
q: InfluxDbReadQuery,
8388
) -> Box<dyn Future<Item = Option<Vec<InfluxDbSeries<T>>>, Error = InfluxDbError>>
8489
where
85-
Q: InfluxDbQuery,
8690
T: DeserializeOwned,
8791
{
8892
use futures::future;
8993

90-
let q_type = q.get_type();
91-
let query = match q.build() {
92-
Err(err) => {
94+
let query = q.build().unwrap();
95+
96+
let client = {
97+
let read_query = query.get();
98+
let encoded: String = form_urlencoded::Serializer::new(String::new())
99+
.append_pair("db", self.database_name())
100+
.append_pair("q", &read_query)
101+
.finish();
102+
let http_query_string = format!(
103+
"{url}/query?{encoded}",
104+
url = self.database_url(),
105+
encoded = encoded
106+
);
107+
108+
if read_query.contains("SELECT") || read_query.contains("SHOW") {
109+
Client::new().get(http_query_string.as_str())
110+
} else {
93111
let error = InfluxDbError::InvalidQueryError {
94-
error: format!("{}", err),
112+
error: String::from(
113+
"Only SELECT and SHOW queries supported with JSON deserialization",
114+
),
95115
};
96116
return Box::new(
97117
future::err::<Option<Vec<InfluxDbSeries<T>>>, InfluxDbError>(error),
98118
);
99119
}
100-
Ok(query) => query,
101-
};
102-
103-
let client = match q_type {
104-
QueryType::ReadQuery => {
105-
let read_query = query.get();
106-
let http_query_string = format!(
107-
"{url}/query?db={db}&q={read_query}",
108-
url = self.database_url(),
109-
db = self.database_name(),
110-
read_query = read_query,
111-
);
112-
113-
if read_query.contains("SELECT") || read_query.contains("SHOW") {
114-
Client::new().get(http_query_string.as_str())
115-
} else {
116-
Client::new().post(http_query_string.as_str())
117-
}
118-
}
119-
QueryType::WriteQuery => Client::new()
120-
.post(
121-
format!(
122-
"{url}/write?db={db}",
123-
url = self.database_url(),
124-
db = self.database_name(),
125-
)
126-
.as_str(),
127-
)
128-
.body(query.get()),
129120
};
130121

131122
Box::new(

Diff for: src/query/mod.rs

+26
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,14 @@ impl ValidQuery {
8989
self.0
9090
}
9191
}
92+
impl<T> From<T> for ValidQuery
93+
where
94+
T: ToString,
95+
{
96+
fn from(string: T) -> Self {
97+
Self(string.to_string())
98+
}
99+
}
92100
impl PartialEq<String> for ValidQuery {
93101
fn eq(&self, other: &String) -> bool {
94102
&self.0 == other
@@ -105,3 +113,21 @@ pub enum QueryType {
105113
ReadQuery,
106114
WriteQuery,
107115
}
116+
117+
#[cfg(test)]
118+
mod tests {
119+
use super::ValidQuery;
120+
121+
#[test]
122+
fn test_equality_str() {
123+
assert_eq!(ValidQuery::from("hello"), "hello");
124+
}
125+
126+
#[test]
127+
fn test_equality_string() {
128+
assert_eq!(
129+
ValidQuery::from(String::from("hello")),
130+
String::from("hello")
131+
);
132+
}
133+
}

Diff for: tests/integration_tests.rs

+47-1
Original file line numberDiff line numberDiff line change
@@ -90,10 +90,56 @@ fn test_json_query() {
9090

9191
let client = create_client();
9292
let query = InfluxDbQuery::raw_read_query("SELECT * FROM weather");
93-
let result = get_runtime().block_on(client.json_query::<Weather, _>(query));
93+
let result = get_runtime().block_on(client.json_query::<Weather>(query));
9494

9595
assert!(
9696
result.is_ok(),
9797
format!("We couldn't read from the DB: {}", result.unwrap_err())
9898
);
9999
}
100+
101+
#[test]
102+
#[cfg(feature = "use-serde")]
103+
/// INTEGRATION TEST
104+
///
105+
/// This integration test tests whether using the wrong query method fails building the query
106+
fn test_serde_query_build_error() {
107+
use serde::Deserialize;
108+
109+
#[derive(Deserialize, Debug)]
110+
struct Weather {
111+
time: String,
112+
temperature: i32,
113+
}
114+
115+
let client = create_client();
116+
let query = InfluxDbQuery::raw_read_query("CREATE database should_fail");
117+
let result = get_runtime().block_on(client.json_query::<Weather>(query));
118+
119+
assert!(
120+
result.is_err(),
121+
format!(
122+
"Should not be able to build JSON query that is not SELECT or SELECT .. INTO: {}",
123+
result.unwrap_err()
124+
)
125+
);
126+
}
127+
128+
#[test]
129+
#[cfg(feature = "use-serde")]
130+
/// INTEGRATION TEST
131+
///
132+
/// This test case tests whether JSON can be decoded from a InfluxDB response
133+
fn test_raw_query_build_error() {
134+
let client = create_client();
135+
let query = InfluxDbQuery::write_query("weather").add_tag("season", "summer");
136+
let result = get_runtime().block_on(client.query(query));
137+
138+
assert!(
139+
result.is_err(),
140+
format!(
141+
"Should not be able to build JSON query that is not SELECT or SELECT .. INTO: {}",
142+
result.unwrap_err()
143+
)
144+
);
145+
}

0 commit comments

Comments
 (0)