diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml
index 00a7eef..209fcae 100644
--- a/.github/workflows/rust.yml
+++ b/.github/workflows/rust.yml
@@ -12,23 +12,18 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- - uses: actions-rs/toolchain@v1
- with:
- toolchain: nightly
- override: true
+ - uses: dtolnay/rust-toolchain@nightly
- run: bash ./auxiliary/update_cargo-readme.sh
- run: bash ./auxiliary/check_readme_consistency.sh
style:
name: Style Checks (stable/ubuntu-latest)
runs-on: ubuntu-latest
-
steps:
- - uses: actions/checkout@master
- - uses: hecrj/setup-rust-action@v1
+ - uses: actions/checkout@v1
+ - uses: dtolnay/rust-toolchain@nightly
with:
components: "rustfmt,clippy"
- - uses: actions/checkout@v1
- name: Check code formatting
run: cargo fmt --all -- --check
- name: Check Clippy lints
@@ -39,27 +34,30 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
- rust_release: [nightly, stable]
+ rust_release: [1.45, stable, nightly]
os: [ubuntu-latest, windows-latest, macOS-latest]
steps:
- - uses: actions/checkout@master
- - uses: hecrj/setup-rust-action@v1
+ - uses: actions/checkout@v1
+ - uses: dtolnay/rust-toolchain@master
with:
- rust-version: ${{ matrix.rust_release }}
+ toolchain: ${{ matrix.rust_release }}
- name: Build
run: cargo build --verbose
integration_test:
name: Integration Tests (stable/ubuntu-latest)
runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ http-backend: [curl-client, h1-client, h1-client-rustls, hyper-client]
services:
influxdb:
- image: influxdb
+ image: influxdb:1.8
ports:
- 8086:8086
authed_influxdb:
- image: influxdb
+ image: influxdb:1.8
ports:
- 9086:8086
env:
@@ -71,11 +69,97 @@ jobs:
steps:
- uses: actions/checkout@v1
- - uses: actions-rs/toolchain@v1
- with:
- toolchain: nightly
- override: true
- - uses: actions-rs/cargo@v1
- with:
- command: test
- args: --package influxdb --package influxdb_derive --all-features --no-fail-fast
\ No newline at end of file
+ - uses: dtolnay/rust-toolchain@stable
+ - run: cargo test --manifest-path=./influxdb/Cargo.toml --no-default-features --features 'use-serde derive ${{ matrix.http-backend }}' --no-fail-fast
+
+ coverage:
+ name: Code Coverage (stable/ubuntu-20.04)
+ runs-on: ubuntu-20.04
+ services:
+ influxdb:
+ image: influxdb:1.8
+ ports:
+ - 8086:8086
+ authed_influxdb:
+ image: influxdb:1.8
+ ports:
+ - 9086:8086
+ env:
+ INFLUXDB_HTTP_AUTH_ENABLED: true
+ INFLUXDB_ADMIN_USER: admin
+ INFLUXDB_ADMIN_PASSWORD: password
+ INFLUXDB_USER: nopriv_user
+ INFLUXDB_USER_PASSWORD: password
+
+ steps:
+ - uses: actions/checkout@v2
+ - uses: dtolnay/rust-toolchain@stable
+
+ - name: Get Rust Version
+ id: rust-version
+ run: echo "::set-output name=VERSION::$(cargo -V | head -n1 | awk '{print $2}')"
+
+ - name: Get Tarpaulin Version
+ id: tarpaulin-version
+ run: echo "::set-output name=VERSION::$(wget -qO- 'https://api.github.com/repos/xd009642/tarpaulin/releases/latest' | jq -r '.tag_name')"
+
+ - uses: actions/cache@v2
+ with:
+ path: |
+ ~/.cargo/bin/cargo-tarpaulin
+ ~/.cargo/git
+ ~/.cargo/registry
+ target
+ key: ${{ runner.os }}-cargo-${{ steps.rust-version.outputs.VERSION }}-tarpaulin-${{ steps.tarpaulin-version.outputs.VERSION }} }}
+
+ - name: Install Tarpaulin
+ run: |
+ ls -lh ~/.cargo/bin
+ test -e ~/.cargo/bin/cargo-tarpaulin || cargo install cargo-tarpaulin --version ${{ steps.tarpaulin-version.outputs.VERSION }}
+
+ - name: Run Tarpaulin coverage tests
+ run: |
+ cargo tarpaulin -v \
+ --target-dir target/tarpaulin \
+ --workspace \
+ --all-features \
+ --exclude-files 'derive/*' \
+ --exclude-files 'target/*' \
+ --ignore-panics --ignore-tests \
+ --out Html --out Json
+ env:
+ RUST_BACKTRACE: 1
+ RUST_LOG: info
+
+ - uses: actions/upload-artifact@v2
+ with:
+ name: tarpaulin-report
+ path: |
+ tarpaulin-report.json
+ tarpaulin-report.html
+
+ pages:
+ runs-on: ubuntu-20.04
+ needs:
+ - coverage
+ if: github.ref == 'refs/heads/master'
+ steps:
+ - uses: actions/checkout@v2
+ with:
+ ref: gh-pages
+
+ - uses: actions/download-artifact@v2
+ with:
+ name: tarpaulin-report
+
+ - run: |
+ coverage=$(jq '.files | { covered: map(.covered) | add, coverable: map(.coverable) | add } | .covered / .coverable * 10000 | round | . / 100' tarpaulin-report.json)
+ color=$([[ $coverage < 80 ]] && printf yellow || printf brightgreen)
+ wget -qO coverage.svg "https://img.shields.io/badge/coverage-$coverage%25-$color"
+
+ git add coverage.svg tarpaulin-report.html
+ git status
+
+ - uses: stefanzweifel/git-auto-commit-action@v4
+ with:
+ commit_message: "GitHub Pages for ${{ github.sha }}"
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 19a0c1f..920eb58 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,46 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
+## [0.4.0] - 2021-03-08
+
+### Fixed
+
+- Fix deserialization of Series in case of no values ([@JEnoch](https://github.com/JEnoch) in [#75](https://github.com/Empty2k12/influxdb-rust/pull/75))
+
+### Added
+
+- implement `#[influxdb(tag)]` and `#[influxdb(ignore)]` ([@blasrodri](https://github.com/blasrodri) in [#81](https://github.com/Empty2k12/influxdb-rust/pull/81))
+
+ `#[tag]` is now `#[influxdb(tag)]` and fields can be ignored with `#[influxdb(ignore)]`
+
+- Batch write support ([@sunng87](https://github.com/sunng87) in [#87](https://github.com/Empty2k12/influxdb-rust/pull/87))
+
+ Build a query containing mutliple rows to be inserted
+ ```
+ let q0 = Timestamp::Hours(11)
+ .into_query("weather")
+ .add_field("temperature", 82)
+ .add_tag("location", "us-midwest");
+
+ let q1 = Timestamp::Hours(12)
+ .into_query("weather")
+ .add_field("temperature", 65)
+ .add_tag("location", "us-midwest");
+
+ let query = vec![q0, q1].build();
+ ```
+
+### Changed
+
+- Assertion should consider case-insensitive keywords ([@rcastill](https://github.com/rcastill) in [#83](https://github.com/Empty2k12/influxdb-rust/pull/83))
+
+ `#[tag]` is now `#[influxdb(tag)]` and fields can be ignored with `#[influxdb(ignore)]`
+
+- Add h1-client-rustls feature ([@JEnoch](https://github.com/JEnoch) in [#88](https://github.com/Empty2k12/influxdb-rust/pull/88))
+
+ Switch between multiple http backends as described in the [README.md](README.md#Choice-of-HTTP-backend)
+
+
## [0.3.0] - 2020-11-15
### Changed
@@ -120,7 +160,8 @@ This release removes the prefix `InfluxDb` of most types in this library and ree
- Improved Test Coverage: There's now even more tests verifying correctness of the crate (#5)
- It's no longer necessary to supply a wildcard generic when working with serde*integration: `client.json_query::(query)` instead of `client.json_query::(query)`
-[unreleased]: https://github.com/Empty2k12/influxdb-rust/compare/v0.3.0...HEAD
+[unreleased]: https://github.com/Empty2k12/influxdb-rust/compare/v0.4.0...HEAD
+[0.4.0]: https://github.com/Empty2k12/influxdb-rust/compare/v0.3.0...v0.4.0
[0.3.0]: https://github.com/Empty2k12/influxdb-rust/compare/v0.2.0...v0.3.0
[0.2.0]: https://github.com/Empty2k12/influxdb-rust/compare/v0.1.0...v0.2.0
[0.1.0]: https://github.com/Empty2k12/influxdb-rust/compare/v0.0.6...v0.1.0
diff --git a/README.md b/README.md
index 4c2c669..96b4aa1 100644
--- a/README.md
+++ b/README.md
@@ -16,21 +16,24 @@
+
+
+
-
-
+
+
This library is a work in progress. This means a feature you might need is not implemented
yet or could be handled better.
-Pull requests are always welcome. See [Contributing](https://github.com/Empty2k12/influxdb-rust/blob/master/CONTRIBUTING.md) and [Code of Conduct](https://github.com/Empty2k12/influxdb-rust/blob/master/CODE_OF_CONDUCT.md).
+Pull requests are always welcome. See [Contributing](https://github.com/Empty2k12/influxdb-rust/blob/master/CONTRIBUTING.md) and [Code of Conduct](https://github.com/Empty2k12/influxdb-rust/blob/master/CODE_OF_CONDUCT.md). For a list of past changes, see [CHANGELOG.md](https://github.com/Empty2k12/influxdb-rust/blob/master/CHANGELOG.md).
### Currently Supported Features
@@ -42,13 +45,14 @@ Pull requests are always welcome. See [Contributing](https://github.com/Empty2k1
- `#[derive(InfluxDbWriteable)]` Derive Macro for Writing / Reading into Structs
- `GROUP BY` support
- Tokio and async-std support (see example below) or [available backends](https://github.com/Empty2k12/influxdb-rust/blob/master/influxdb/Cargo.toml)
+- Swappable HTTP backends ([see below](#Choice-of-HTTP-backend))
## Quickstart
Add the following to your `Cargo.toml`
```toml
-influxdb = { version = "0.3.0", features = ["derive"] }
+influxdb = { version = "0.4.0", features = ["derive"] }
```
For an example with using Serde deserialization, please refer to [serde_integration](crate::integrations::serde_integration)
@@ -68,7 +72,7 @@ async fn main() {
struct WeatherReading {
time: DateTime,
humidity: i32,
- #[tag] wind_direction: String,
+ #[influxdb(tag)] wind_direction: String,
}
// Let's write some data into a measurement called `weather`
@@ -95,6 +99,31 @@ async fn main() {
For further examples, check out the Integration Tests in `tests/integration_tests.rs`
in the repository.
+## Choice of HTTP backend
+
+To communicate with InfluxDB, you can choose the HTTP backend to be used configuring the appropriate feature:
+
+- **[hyper](https://github.com/hyperium/hyper)** (used by default)
+ ```toml
+ influxdb = { version = "0.4.0", features = ["derive"] }
+ ```
+- **[curl](https://github.com/alexcrichton/curl-rust)**, using [libcurl](https://curl.se/libcurl/)
+ ```toml
+ influxdb = { version = "0.4.0", default-features = false, features = ["derive", "use-serde", "curl-client"] }
+ ```
+- **[async-h1](https://github.com/http-rs/async-h1)** with native TLS (OpenSSL)
+ ```toml
+ influxdb = { version = "0.4.0", default-features = false, features = ["derive", "use-serde", "h1-client"] }
+ ```
+- **[async-h1](https://github.com/http-rs/async-h1)** with [rustls](https://github.com/ctz/rustls)
+ ```toml
+ influxdb = { version = "0.4.0", default-features = false, features = ["derive", "use-serde", "h1-client-rustls"] }
+ ```
+- WebAssembly's `window.fetch`, via `web-sys` and **[wasm-bindgen](https://github.com/rustwasm/wasm-bindgen)**
+ ```toml
+ influxdb = { version = "0.4.0", default-features = false, features = ["derive", "use-serde", "wasm-client"] }
+ ```
+
## License
[](https://opensource.org/licenses/MIT)
diff --git a/README.tpl b/README.tpl
index 950b79b..0647cd3 100644
--- a/README.tpl
+++ b/README.tpl
@@ -16,14 +16,17 @@
+
+
+
-
-
+
+
diff --git a/benches/client.rs b/benches/client.rs
index a94c987..2b8ca5d 100644
--- a/benches/client.rs
+++ b/benches/client.rs
@@ -11,7 +11,7 @@ use tokio::sync::Semaphore;
struct WeatherReading {
time: DateTime,
humidity: i32,
- #[tag]
+ #[influxdb(tag)]
wind_direction: String,
}
diff --git a/influxdb/Cargo.toml b/influxdb/Cargo.toml
index 637a5b7..6ebbf5a 100644
--- a/influxdb/Cargo.toml
+++ b/influxdb/Cargo.toml
@@ -2,7 +2,7 @@
[package]
name = "influxdb"
-version = "0.3.0"
+version = "0.4.0"
authors = ["Gero Gerke <11deutron11@gmail.com>"]
edition = "2018"
description = "InfluxDB Driver for Rust"
@@ -19,9 +19,9 @@ travis-ci = { repository = "Empty2k12/influxdb-rust", branch = "master" }
chrono = { version = "0.4.11", features = ["serde"] }
futures = "0.3.4"
lazy_static = "1.4.0"
-influxdb_derive = { version = "0.3.0", optional = true }
+influxdb_derive = { version = "0.4.0", optional = true }
regex = "1.3.5"
-surf = { version = "2.1.0", default-features = false }
+surf = { version = "2.2.0", default-features = false }
serde = { version = "1.0.104", features = ["derive"], optional = true }
serde_json = { version = "1.0.48", optional = true }
thiserror = "1.0"
@@ -30,6 +30,7 @@ thiserror = "1.0"
use-serde = ["serde", "serde_json"]
curl-client = ["surf/curl-client"]
h1-client = ["surf/h1-client"]
+h1-client-rustls = ["surf/h1-client-rustls"]
hyper-client = ["surf/hyper-client"]
wasm-client = ["surf/wasm-client"]
default = ["use-serde", "hyper-client"]
diff --git a/influxdb/src/client/mod.rs b/influxdb/src/client/mod.rs
index 5874978..468b0ae 100644
--- a/influxdb/src/client/mod.rs
+++ b/influxdb/src/client/mod.rs
@@ -18,7 +18,7 @@
use futures::prelude::*;
use surf::{self, Client as SurfClient, StatusCode};
-use crate::query::QueryTypes;
+use crate::query::QueryType;
use crate::Error;
use crate::Query;
use std::collections::HashMap;
@@ -159,14 +159,13 @@ impl Client {
pub async fn query<'q, Q>(&self, q: &'q Q) -> Result
where
Q: Query,
- &'q Q: Into>,
{
let query = q.build().map_err(|err| Error::InvalidQueryError {
error: err.to_string(),
})?;
- let request_builder = match q.into() {
- QueryTypes::Read(_) => {
+ let request_builder = match q.get_type() {
+ QueryType::ReadQuery => {
let read_query = query.get();
let url = &format!("{}/query", &self.url);
let mut parameters = self.parameters.as_ref().clone();
@@ -178,10 +177,10 @@ impl Client {
self.client.post(url).query(¶meters)
}
}
- QueryTypes::Write(write_query) => {
+ QueryType::WriteQuery(precision) => {
let url = &format!("{}/write", &self.url);
let mut parameters = self.parameters.as_ref().clone();
- parameters.insert("precision", write_query.get_precision());
+ parameters.insert("precision", precision);
self.client.post(url).body(query.get()).query(¶meters)
}
diff --git a/influxdb/src/integrations/serde_integration/de.rs b/influxdb/src/integrations/serde_integration/de.rs
index fd9bf99..82eb607 100644
--- a/influxdb/src/integrations/serde_integration/de.rs
+++ b/influxdb/src/integrations/serde_integration/de.rs
@@ -22,11 +22,11 @@ where
Name,
Columns,
Values,
- };
+ }
struct SeriesVisitor {
_inner_type: PhantomData,
- };
+ }
impl<'de, T> Visitor<'de> for SeriesVisitor
where
@@ -80,7 +80,8 @@ where
}
}
let name = name.ok_or_else(|| Error::missing_field("name"))?;
- let values = values.ok_or_else(|| Error::missing_field("values"))?;
+ let values = values.unwrap_or_default();
+
Ok(Series { name, values })
}
}
@@ -114,12 +115,12 @@ where
Tags,
Columns,
Values,
- };
+ }
struct SeriesVisitor {
_tag_type: PhantomData,
_value_type: PhantomData,
- };
+ }
impl<'de, TAG, T> Visitor<'de> for SeriesVisitor
where
diff --git a/influxdb/src/integrations/serde_integration/mod.rs b/influxdb/src/integrations/serde_integration/mod.rs
index 7168469..3a734af 100644
--- a/influxdb/src/integrations/serde_integration/mod.rs
+++ b/influxdb/src/integrations/serde_integration/mod.rs
@@ -129,8 +129,9 @@ impl Client {
})?;
let read_query = query.get();
+ let read_query_lower = read_query.to_lowercase();
- if !read_query.contains("SELECT") && !read_query.contains("SHOW") {
+ if !read_query_lower.contains("select") && !read_query_lower.contains("show") {
let error = Error::InvalidQueryError {
error: String::from(
"Only SELECT and SHOW queries supported with JSON deserialization",
diff --git a/influxdb/src/lib.rs b/influxdb/src/lib.rs
index 2e5eebb..117f07f 100644
--- a/influxdb/src/lib.rs
+++ b/influxdb/src/lib.rs
@@ -1,7 +1,7 @@
//! This library is a work in progress. This means a feature you might need is not implemented
//! yet or could be handled better.
//!
-//! Pull requests are always welcome. See [Contributing](https://github.com/Empty2k12/influxdb-rust/blob/master/CONTRIBUTING.md) and [Code of Conduct](https://github.com/Empty2k12/influxdb-rust/blob/master/CODE_OF_CONDUCT.md).
+//! Pull requests are always welcome. See [Contributing](https://github.com/Empty2k12/influxdb-rust/blob/master/CONTRIBUTING.md) and [Code of Conduct](https://github.com/Empty2k12/influxdb-rust/blob/master/CODE_OF_CONDUCT.md). For a list of past changes, see [CHANGELOG.md](https://github.com/Empty2k12/influxdb-rust/blob/master/CHANGELOG.md).
//!
//! ## Currently Supported Features
//!
@@ -13,13 +13,14 @@
//! - `#[derive(InfluxDbWriteable)]` Derive Macro for Writing / Reading into Structs
//! - `GROUP BY` support
//! - Tokio and async-std support (see example below) or [available backends](https://github.com/Empty2k12/influxdb-rust/blob/master/influxdb/Cargo.toml)
+//! - Swappable HTTP backends ([see below](#Choice-of-HTTP-backend))
//!
//! # Quickstart
//!
//! Add the following to your `Cargo.toml`
//!
//! ```toml
-//! influxdb = { version = "0.3.0", features = ["derive"] }
+//! influxdb = { version = "0.4.0", features = ["derive"] }
//! ```
//!
//! For an example with using Serde deserialization, please refer to [serde_integration](crate::integrations::serde_integration)
@@ -39,7 +40,7 @@
//! struct WeatherReading {
//! time: DateTime,
//! humidity: i32,
-//! #[tag] wind_direction: String,
+//! #[influxdb(tag)] wind_direction: String,
//! }
//!
//! // Let's write some data into a measurement called `weather`
@@ -66,6 +67,31 @@
//! For further examples, check out the Integration Tests in `tests/integration_tests.rs`
//! in the repository.
//!
+//! # Choice of HTTP backend
+//!
+//! To communicate with InfluxDB, you can choose the HTTP backend to be used configuring the appropriate feature:
+//!
+//! - **[hyper](https://github.com/hyperium/hyper)** (used by default)
+//! ```toml
+//! influxdb = { version = "0.4.0", features = ["derive"] }
+//! ```
+//! - **[curl](https://github.com/alexcrichton/curl-rust)**, using [libcurl](https://curl.se/libcurl/)
+//! ```toml
+//! influxdb = { version = "0.4.0", default-features = false, features = ["derive", "use-serde", "curl-client"] }
+//! ```
+//! - **[async-h1](https://github.com/http-rs/async-h1)** with native TLS (OpenSSL)
+//! ```toml
+//! influxdb = { version = "0.4.0", default-features = false, features = ["derive", "use-serde", "h1-client"] }
+//! ```
+//! - **[async-h1](https://github.com/http-rs/async-h1)** with [rustls](https://github.com/ctz/rustls)
+//! ```toml
+//! influxdb = { version = "0.4.0", default-features = false, features = ["derive", "use-serde", "h1-client-rustls"] }
+//! ```
+//! - WebAssembly's `window.fetch`, via `web-sys` and **[wasm-bindgen](https://github.com/rustwasm/wasm-bindgen)**
+//! ```toml
+//! influxdb = { version = "0.4.0", default-features = false, features = ["derive", "use-serde", "wasm-client"] }
+//! ```
+//!
//! # License
//!
//! [](https://opensource.org/licenses/MIT)
@@ -82,7 +108,7 @@ pub use error::Error;
pub use query::{
read_query::ReadQuery,
write_query::{Type, WriteQuery},
- InfluxDbWriteable, Query, QueryType, QueryTypes, Timestamp, ValidQuery,
+ InfluxDbWriteable, Query, QueryType, Timestamp, ValidQuery,
};
#[cfg(feature = "use-serde")]
diff --git a/influxdb/src/query/mod.rs b/influxdb/src/query/mod.rs
index 8fb0c0b..de08f19 100644
--- a/influxdb/src/query/mod.rs
+++ b/influxdb/src/query/mod.rs
@@ -55,9 +55,9 @@ impl fmt::Display for Timestamp {
}
}
-impl Into> for Timestamp {
- fn into(self) -> DateTime {
- match self {
+impl From for DateTime {
+ fn from(ts: Timestamp) -> DateTime {
+ match ts {
Timestamp::Hours(h) => {
let nanos =
h * MINUTES_PER_HOUR * SECONDS_PER_MINUTE * MILLIS_PER_SECOND * NANOS_PER_MILLI;
@@ -93,24 +93,6 @@ where
}
}
-/// Internal enum used to represent either type of query.
-pub enum QueryTypes<'a> {
- Read(&'a ReadQuery),
- Write(&'a WriteQuery),
-}
-
-impl<'a> From<&'a ReadQuery> for QueryTypes<'a> {
- fn from(query: &'a ReadQuery) -> Self {
- Self::Read(query)
- }
-}
-
-impl<'a> From<&'a WriteQuery> for QueryTypes<'a> {
- fn from(query: &'a WriteQuery) -> Self {
- Self::Write(query)
- }
-}
-
pub trait Query {
/// Builds valid InfluxSQL which can be run against the Database.
/// In case no fields have been specified, it will return an error,
@@ -192,7 +174,8 @@ impl PartialEq<&str> for ValidQuery {
#[derive(PartialEq, Debug)]
pub enum QueryType {
ReadQuery,
- WriteQuery,
+ /// write query with precision
+ WriteQuery(String),
}
#[cfg(test)]
diff --git a/influxdb/src/query/write_query.rs b/influxdb/src/query/write_query.rs
index 51ea723..cc5fd0c 100644
--- a/influxdb/src/query/write_query.rs
+++ b/influxdb/src/query/write_query.rs
@@ -205,7 +205,29 @@ impl Query for WriteQuery {
}
fn get_type(&self) -> QueryType {
- QueryType::WriteQuery
+ QueryType::WriteQuery(self.get_precision())
+ }
+}
+
+impl Query for Vec {
+ fn build(&self) -> Result {
+ let mut qlines = Vec::new();
+
+ for q in self {
+ let valid_query = q.build()?;
+ qlines.push(valid_query.0);
+ }
+
+ Ok(ValidQuery(qlines.join("\n")))
+ }
+
+ fn get_type(&self) -> QueryType {
+ QueryType::WriteQuery(
+ self.get(0)
+ .map(|q| q.get_precision())
+ // use "ms" as placeholder if query is empty
+ .unwrap_or_else(|| "ms".to_owned()),
+ )
}
}
@@ -296,7 +318,7 @@ mod tests {
.add_tag("location", "us-midwest")
.add_tag("season", "summer");
- assert_eq!(query.get_type(), QueryType::WriteQuery);
+ assert_eq!(query.get_type(), QueryType::WriteQuery("h".to_owned()));
}
#[test]
@@ -318,4 +340,25 @@ mod tests {
r#"wea\,\ ther=,location=us-midwest,loc\,\ \="ation=us\,\ \"mid\=west temperature=82i,"temp\=era\,t\ ure"="too\"\\\\hot",float=82 11"#
);
}
+
+ #[test]
+ fn test_batch() {
+ let q0 = Timestamp::Hours(11)
+ .into_query("weather")
+ .add_field("temperature", 82)
+ .add_tag("location", "us-midwest");
+
+ let q1 = Timestamp::Hours(12)
+ .into_query("weather")
+ .add_field("temperature", 65)
+ .add_tag("location", "us-midwest");
+
+ let query = vec![q0, q1].build();
+
+ assert_eq!(
+ query.unwrap().get(),
+ r#"weather,location=us-midwest temperature=82i 11
+weather,location=us-midwest temperature=65i 12"#
+ );
+ }
}
diff --git a/influxdb/tests/derive_integration_tests.rs b/influxdb/tests/derive_integration_tests.rs
index 9c136b2..463fed9 100644
--- a/influxdb/tests/derive_integration_tests.rs
+++ b/influxdb/tests/derive_integration_tests.rs
@@ -14,11 +14,20 @@ use utilities::{assert_result_ok, create_client, create_db, delete_db, run_test}
#[derive(Debug, PartialEq)]
#[cfg_attr(feature = "derive", derive(InfluxDbWriteable))]
-#[cfg_attr(feature = "use-serde", derive(Deserialize))]
struct WeatherReading {
time: DateTime,
+ #[influxdb(ignore)]
humidity: i32,
- #[tag]
+ pressure: i32,
+ #[influxdb(tag)]
+ wind_strength: Option,
+}
+
+#[derive(Debug)]
+#[cfg_attr(feature = "use-serde", derive(Deserialize))]
+struct WeatherReadingWithoutIgnored {
+ time: DateTime,
+ pressure: i32,
wind_strength: Option,
}
@@ -27,15 +36,14 @@ fn test_build_query() {
let weather_reading = WeatherReading {
time: Timestamp::Hours(1).into(),
humidity: 30,
+ pressure: 100,
wind_strength: Some(5),
};
- let query = weather_reading
- .into_query("weather_reading")
- .build()
- .unwrap();
+ let query = weather_reading.into_query("weather_reading");
+ let query = query.build().unwrap();
assert_eq!(
query.get(),
- "weather_reading,wind_strength=5 humidity=30i 3600000000000"
+ "weather_reading,wind_strength=5 pressure=100i 3600000000000"
);
}
@@ -44,6 +52,7 @@ fn test_build_query() {
///
/// This integration tests that writing data and retrieving the data again is working
#[async_std::test]
+#[cfg(not(tarpaulin_include))]
async fn test_derive_simple_write() {
const TEST_NAME: &str = "test_derive_simple_write";
@@ -55,6 +64,7 @@ async fn test_derive_simple_write() {
time: Timestamp::Nanoseconds(0).into(),
humidity: 30,
wind_strength: Some(5),
+ pressure: 100,
};
let query = weather_reading.into_query("weather_reading");
let result = client.query(&query).await;
@@ -73,6 +83,7 @@ async fn test_derive_simple_write() {
#[cfg(feature = "derive")]
#[cfg(feature = "use-serde")]
#[async_std::test]
+#[cfg(not(tarpaulin_include))]
async fn test_write_and_read_option() {
const TEST_NAME: &str = "test_write_and_read_option";
@@ -84,20 +95,21 @@ async fn test_write_and_read_option() {
time: Timestamp::Hours(11).into(),
humidity: 30,
wind_strength: None,
+ pressure: 100,
};
let write_result = client
.query(&weather_reading.into_query("weather_reading".to_string()))
.await;
assert_result_ok(&write_result);
let query =
- Query::raw_read_query("SELECT time, humidity, wind_strength FROM weather_reading");
+ Query::raw_read_query("SELECT time, pressure, wind_strength FROM weather_reading");
let result = client.json_query(query).await.and_then(|mut db_result| {
println!("{:?}", db_result);
- db_result.deserialize_next::()
+ db_result.deserialize_next::()
});
assert_result_ok(&result);
let result = result.unwrap();
- assert_eq!(result.series[0].values[0].humidity, 30);
+ assert_eq!(result.series[0].values[0].pressure, 100);
assert_eq!(result.series[0].values[0].wind_strength, None);
},
|| async move {
diff --git a/influxdb/tests/integration_tests.rs b/influxdb/tests/integration_tests.rs
index 47bb1d1..d92f405 100644
--- a/influxdb/tests/integration_tests.rs
+++ b/influxdb/tests/integration_tests.rs
@@ -13,6 +13,7 @@ use influxdb::{Client, Error, Query, Timestamp};
///
/// This test case tests whether the InfluxDB server can be connected to and gathers info about it - tested with async_std
#[async_std::test]
+#[cfg(not(tarpaulin_include))]
async fn test_ping_influx_db_async_std() {
let client = create_client("notusedhere");
let result = client.ping().await;
@@ -29,6 +30,7 @@ async fn test_ping_influx_db_async_std() {
///
/// This test case tests whether the InfluxDB server can be connected to and gathers info about it * tested with tokio
#[tokio::test]
+#[cfg(not(tarpaulin_include))]
async fn test_ping_influx_db_tokio() {
let client = create_client("notusedhere");
let result = client.ping().await;
@@ -45,19 +47,20 @@ async fn test_ping_influx_db_tokio() {
///
/// This test case tests connection error
#[async_std::test]
+#[cfg(not(tarpaulin_include))]
async fn test_connection_error() {
let test_name = "test_connection_error";
let client =
- Client::new("http://localhost:10086", test_name).with_auth("nopriv_user", "password");
+ Client::new("http://127.0.0.1:10086", test_name).with_auth("nopriv_user", "password");
let read_query = Query::raw_read_query("SELECT * FROM weather");
let read_result = client.query(&read_query).await;
assert_result_err(&read_result);
match read_result {
Err(Error::ConnectionError { .. }) => {}
- _ => panic!(format!(
+ _ => panic!(
"Should cause a ConnectionError: {}",
read_result.unwrap_err()
- )),
+ ),
}
}
@@ -65,13 +68,14 @@ async fn test_connection_error() {
///
/// This test case tests the Authentication
#[async_std::test]
+#[cfg(not(tarpaulin_include))]
async fn test_authed_write_and_read() {
const TEST_NAME: &str = "test_authed_write_and_read";
run_test(
|| async move {
let client =
- Client::new("http://localhost:9086", TEST_NAME).with_auth("admin", "password");
+ Client::new("http://127.0.0.1:9086", TEST_NAME).with_auth("admin", "password");
let query = format!("CREATE DATABASE {}", TEST_NAME);
client
.query(&Query::raw_read_query(query))
@@ -79,7 +83,7 @@ async fn test_authed_write_and_read() {
.expect("could not setup db");
let client =
- Client::new("http://localhost:9086", TEST_NAME).with_auth("admin", "password");
+ Client::new("http://127.0.0.1:9086", TEST_NAME).with_auth("admin", "password");
let write_query = Timestamp::Hours(11)
.into_query("weather")
.add_field("temperature", 82);
@@ -96,7 +100,7 @@ async fn test_authed_write_and_read() {
},
|| async move {
let client =
- Client::new("http://localhost:9086", TEST_NAME).with_auth("admin", "password");
+ Client::new("http://127.0.0.1:9086", TEST_NAME).with_auth("admin", "password");
let query = format!("DROP DATABASE {}", TEST_NAME);
client
@@ -112,13 +116,14 @@ async fn test_authed_write_and_read() {
///
/// This test case tests the Authentication
#[async_std::test]
+#[cfg(not(tarpaulin_include))]
async fn test_wrong_authed_write_and_read() {
const TEST_NAME: &str = "test_wrong_authed_write_and_read";
run_test(
|| async move {
let client =
- Client::new("http://localhost:9086", TEST_NAME).with_auth("admin", "password");
+ Client::new("http://127.0.0.1:9086", TEST_NAME).with_auth("admin", "password");
let query = format!("CREATE DATABASE {}", TEST_NAME);
client
.query(&Query::raw_read_query(query))
@@ -126,7 +131,7 @@ async fn test_wrong_authed_write_and_read() {
.expect("could not setup db");
let client =
- Client::new("http://localhost:9086", TEST_NAME).with_auth("wrong_user", "password");
+ Client::new("http://127.0.0.1:9086", TEST_NAME).with_auth("wrong_user", "password");
let write_query = Timestamp::Hours(11)
.into_query("weather")
.add_field("temperature", 82);
@@ -134,10 +139,10 @@ async fn test_wrong_authed_write_and_read() {
assert_result_err(&write_result);
match write_result {
Err(Error::AuthorizationError) => {}
- _ => panic!(format!(
+ _ => panic!(
"Should be an AuthorizationError: {}",
write_result.unwrap_err()
- )),
+ ),
}
let read_query = Query::raw_read_query("SELECT * FROM weather");
@@ -145,28 +150,28 @@ async fn test_wrong_authed_write_and_read() {
assert_result_err(&read_result);
match read_result {
Err(Error::AuthorizationError) => {}
- _ => panic!(format!(
+ _ => panic!(
"Should be an AuthorizationError: {}",
read_result.unwrap_err()
- )),
+ ),
}
- let client = Client::new("http://localhost:9086", TEST_NAME)
+ let client = Client::new("http://127.0.0.1:9086", TEST_NAME)
.with_auth("nopriv_user", "password");
let read_query = Query::raw_read_query("SELECT * FROM weather");
let read_result = client.query(&read_query).await;
assert_result_err(&read_result);
match read_result {
Err(Error::AuthenticationError) => {}
- _ => panic!(format!(
+ _ => panic!(
"Should be an AuthenticationError: {}",
read_result.unwrap_err()
- )),
+ ),
}
},
|| async move {
let client =
- Client::new("http://localhost:9086", TEST_NAME).with_auth("admin", "password");
+ Client::new("http://127.0.0.1:9086", TEST_NAME).with_auth("admin", "password");
let query = format!("DROP DATABASE {}", TEST_NAME);
client
.query(&Query::raw_read_query(query))
@@ -181,19 +186,20 @@ async fn test_wrong_authed_write_and_read() {
///
/// This test case tests the Authentication
#[async_std::test]
+#[cfg(not(tarpaulin_include))]
async fn test_non_authed_write_and_read() {
const TEST_NAME: &str = "test_non_authed_write_and_read";
run_test(
|| async move {
let client =
- Client::new("http://localhost:9086", TEST_NAME).with_auth("admin", "password");
+ Client::new("http://127.0.0.1:9086", TEST_NAME).with_auth("admin", "password");
let query = format!("CREATE DATABASE {}", TEST_NAME);
client
.query(&Query::raw_read_query(query))
.await
.expect("could not setup db");
- let non_authed_client = Client::new("http://localhost:9086", TEST_NAME);
+ let non_authed_client = Client::new("http://127.0.0.1:9086", TEST_NAME);
let write_query = Timestamp::Hours(11)
.into_query("weather")
.add_field("temperature", 82);
@@ -201,10 +207,10 @@ async fn test_non_authed_write_and_read() {
assert_result_err(&write_result);
match write_result {
Err(Error::AuthorizationError) => {}
- _ => panic!(format!(
+ _ => panic!(
"Should be an AuthorizationError: {}",
write_result.unwrap_err()
- )),
+ ),
}
let read_query = Query::raw_read_query("SELECT * FROM weather");
@@ -212,15 +218,15 @@ async fn test_non_authed_write_and_read() {
assert_result_err(&read_result);
match read_result {
Err(Error::AuthorizationError) => {}
- _ => panic!(format!(
+ _ => panic!(
"Should be an AuthorizationError: {}",
read_result.unwrap_err()
- )),
+ ),
}
},
|| async move {
let client =
- Client::new("http://localhost:9086", TEST_NAME).with_auth("admin", "password");
+ Client::new("http://127.0.0.1:9086", TEST_NAME).with_auth("admin", "password");
let query = format!("DROP DATABASE {}", TEST_NAME);
client
.query(&Query::raw_read_query(query))
@@ -235,6 +241,7 @@ async fn test_non_authed_write_and_read() {
///
/// This integration tests that writing data and retrieving the data again is working
#[async_std::test]
+#[cfg(not(tarpaulin_include))]
async fn test_write_and_read_field() {
const TEST_NAME: &str = "test_write_field";
@@ -268,6 +275,7 @@ async fn test_write_and_read_field() {
/// This integration tests that writing data and retrieving the data again is working
#[async_std::test]
#[cfg(feature = "use-serde")]
+#[cfg(not(tarpaulin_include))]
async fn test_write_and_read_option() {
use serde::Deserialize;
@@ -329,6 +337,7 @@ async fn test_write_and_read_option() {
/// is equal to the data which was written to the database
#[async_std::test]
#[cfg(feature = "use-serde")]
+#[cfg(not(tarpaulin_include))]
async fn test_json_query() {
use serde::Deserialize;
@@ -380,6 +389,7 @@ async fn test_json_query() {
// deserialize_next_tagged into a tags struct
#[async_std::test]
#[cfg(feature = "use-serde")]
+#[cfg(not(tarpaulin_include))]
async fn test_json_query_tagged() {
use serde::Deserialize;
@@ -444,6 +454,7 @@ async fn test_json_query_tagged() {
/// (tested with tokio)
#[tokio::test]
#[cfg(feature = "use-serde")]
+#[cfg(not(tarpaulin_include))]
async fn test_json_query_vec() {
use serde::Deserialize;
@@ -494,6 +505,7 @@ async fn test_json_query_vec() {
/// This integration test tests whether using the wrong query method fails building the query
#[async_std::test]
#[cfg(feature = "use-serde")]
+#[cfg(not(tarpaulin_include))]
async fn test_serde_multi_query() {
use serde::Deserialize;
@@ -570,6 +582,7 @@ async fn test_serde_multi_query() {
/// This integration test tests whether using the wrong query method fails building the query
#[async_std::test]
#[cfg(feature = "use-serde")]
+#[cfg(not(tarpaulin_include))]
async fn test_wrong_query_errors() {
let client = create_client("test_name");
let result = client
diff --git a/influxdb/tests/utilities.rs b/influxdb/tests/utilities.rs
index 11a498a..b850d2d 100644
--- a/influxdb/tests/utilities.rs
+++ b/influxdb/tests/utilities.rs
@@ -3,21 +3,25 @@ use influxdb::{Client, Error, Query};
use std::panic::{AssertUnwindSafe, UnwindSafe};
#[allow(dead_code)]
+#[cfg(not(tarpaulin_include))]
pub fn assert_result_err(result: &Result) {
result.as_ref().expect_err("assert_result_err failed");
}
+#[cfg(not(tarpaulin_include))]
pub fn assert_result_ok(result: &Result) {
result.as_ref().expect("assert_result_ok failed");
}
+#[cfg(not(tarpaulin_include))]
pub fn create_client(db_name: T) -> Client
where
T: Into,
{
- Client::new("http://localhost:8086", db_name)
+ Client::new("http://127.0.0.1:8086", db_name)
}
+#[cfg(not(tarpaulin_include))]
pub async fn create_db(name: T) -> Result
where
T: Into,
@@ -29,6 +33,7 @@ where
.await
}
+#[cfg(not(tarpaulin_include))]
pub async fn delete_db(name: T) -> Result
where
T: Into,
@@ -40,6 +45,7 @@ where
.await
}
+#[cfg(not(tarpaulin_include))]
pub async fn run_test(test_fn: F, teardown: T)
where
F: FnOnce() -> Fut1 + UnwindSafe,
diff --git a/influxdb_derive/Cargo.toml b/influxdb_derive/Cargo.toml
index 0fd812b..a2c1e06 100644
--- a/influxdb_derive/Cargo.toml
+++ b/influxdb_derive/Cargo.toml
@@ -2,7 +2,7 @@
[package]
name = "influxdb_derive"
-version = "0.3.0"
+version = "0.4.0"
authors = ["Gero Gerke <11deutron11@gmail.com>"]
edition = "2018"
description = "InfluxDB Driver for Rust - Derive"
diff --git a/influxdb_derive/src/lib.rs b/influxdb_derive/src/lib.rs
index 2bfe464..a6bbba8 100644
--- a/influxdb_derive/src/lib.rs
+++ b/influxdb_derive/src/lib.rs
@@ -1,5 +1,3 @@
-extern crate proc_macro;
-
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
@@ -11,7 +9,7 @@ fn krate() -> TokenStream2 {
quote!(::influxdb)
}
-#[proc_macro_derive(InfluxDbWriteable, attributes(tag))]
+#[proc_macro_derive(InfluxDbWriteable, attributes(influxdb))]
pub fn derive_writeable(tokens: TokenStream) -> TokenStream {
expand_writeable(tokens)
}
diff --git a/influxdb_derive/src/writeable.rs b/influxdb_derive/src/writeable.rs
index f42c333..04a7bb0 100644
--- a/influxdb_derive/src/writeable.rs
+++ b/influxdb_derive/src/writeable.rs
@@ -1,32 +1,79 @@
use proc_macro::TokenStream;
-use proc_macro2::TokenStream as TokenStream2;
+use proc_macro2::{TokenStream as TokenStream2, TokenTree};
use quote::{format_ident, quote};
use syn::{parse_macro_input, Field, Fields, Ident, ItemStruct};
+#[derive(Debug)]
struct WriteableField {
ident: Ident,
is_tag: bool,
+ is_ignore: bool,
}
impl From for WriteableField {
fn from(field: Field) -> WriteableField {
let ident = field.ident.expect("fields without ident are not supported");
- let is_tag = field.attrs.iter().any(|attr| {
+
+ let check_influx_aware = |attr: &syn::Attribute| -> bool {
attr.path
.segments
.iter()
.last()
.map(|seg| seg.ident.to_string())
.unwrap_or_default()
- == "tag"
+ == "influxdb"
+ };
+
+ let check_for_attr = |token_tree, ident_cmp: &str| -> bool {
+ match token_tree {
+ TokenTree::Group(group) => group
+ .stream()
+ .into_iter()
+ .next()
+ .map(|token_tree| match token_tree {
+ TokenTree::Ident(ident) => ident == ident_cmp,
+ _ => false,
+ })
+ .unwrap(),
+ _ => false,
+ }
+ };
+
+ let is_ignore = field.attrs.iter().any(|attr| {
+ if !check_influx_aware(attr) {
+ return false;
+ }
+
+ attr.tokens
+ .clone()
+ .into_iter()
+ .next()
+ .map(|token_tree| check_for_attr(token_tree, "ignore"))
+ .unwrap()
});
- WriteableField { ident, is_tag }
+
+ let is_tag = field.attrs.iter().any(|attr| {
+ if !check_influx_aware(attr) {
+ return false;
+ }
+ attr.tokens
+ .clone()
+ .into_iter()
+ .next()
+ .map(|token_tree| check_for_attr(token_tree, "tag"))
+ .unwrap()
+ });
+
+ WriteableField {
+ ident,
+ is_tag,
+ is_ignore,
+ }
}
}
pub fn expand_writeable(tokens: TokenStream) -> TokenStream {
let krate = super::krate();
-
let input = parse_macro_input!(tokens as ItemStruct);
let ident = input.ident;
let generics = input.generics;
@@ -38,6 +85,7 @@ pub fn expand_writeable(tokens: TokenStream) -> TokenStream {
.named
.into_iter()
.map(WriteableField::from)
+ .filter(|field| !field.is_ignore)
.filter(|field| field.ident.to_string() != time_field.to_string())
.map(|field| {
let ident = field.ident;
pFad - Phonifier reborn
Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies:
Alternative Proxy
pFad Proxy
pFad v3 Proxy
pFad v4 Proxy