diff --git a/.github/workflows/ci-matrix.yml b/.github/workflows/ci-matrix.yml index e60842a..a119db5 100644 --- a/.github/workflows/ci-matrix.yml +++ b/.github/workflows/ci-matrix.yml @@ -14,6 +14,7 @@ jobs: - surf-client # no need for reqwest-client-rustls, it changes only internals of reqwest - reqwest-client + - reqwest-client-11 steps: - uses: actions/checkout@v2 @@ -58,13 +59,13 @@ jobs: - uses: actions-rs/cargo@v1 with: command: build - args: --features strict,surf-client,reqwest-client,reqwest-client-rustls --all-targets + args: --features strict,surf-client,reqwest-client,reqwest-client-rustls,reqwest-client-11,reqwest-client-11-rustls --all-targets - uses: actions-rs/cargo@v1 # We test with approximately all-features to ensure that that does build (excludes nightly only backtrace) with: command: test - args: --features strict,surf-client,reqwest-client,reqwest-client-rustls --all-targets + args: --features strict,surf-client,reqwest-client,reqwest-client-rustls,reqwest-client-11,reqwest-client-11-rustls --all-targets - uses: actions-rs/cargo@v1 with: @@ -74,14 +75,14 @@ jobs: - uses: actions-rs/cargo@v1 with: command: clippy - args: --features strict,surf-client,reqwest-client,reqwest-client-rustls --all-targets -- -D warnings + args: --features strict,surf-client,reqwest-client,reqwest-client-rustls,reqwest-client-11,reqwest-client-11-rustls --all-targets -- -D warnings msrv: runs-on: ubuntu-latest strategy: matrix: rust: - - 1.59.0 # MSRV + - 1.60.0 # MSRV steps: - uses: actions/checkout@v2 diff --git a/Cargo.toml b/Cargo.toml index 3397c9d..3919785 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,8 +10,8 @@ license = "Apache-2.0" name = "unleash-api-client" readme = "README.md" repository = "https://github.com/cognitedata/unleash-client-rust/" -rust-version = "1.59" -version = "0.10.3" +rust-version = "1.60" +version = "0.11.0" [badges] [badges.maintenance] @@ -20,12 +20,16 @@ status = "experimental" [features] # Enable backtrace feature in anyhow backtrace = ["anyhow/backtrace"] -default = ["surf-client"] +# Force clients to make a choice about which client to use +default = [] # Enable the functional test suite functional = [] -reqwest-client = ["reqwest", "reqwest/default-tls"] +# Built in HTTP clients +reqwest-client = ["reqwest", "reqwest?/default-tls"] +reqwest-client-11 = ["reqwest-11", "reqwest-11?/default-tls"] # For users that don't want to depend on OpenSSL. -reqwest-client-rustls = ["reqwest", "reqwest/rustls-tls"] +reqwest-client-11-rustls = ["reqwest-11", "reqwest-11?/rustls-tls"] +reqwest-client-rustls = ["reqwest", "reqwest?/rustls-tls"] # To error if an unsupported API feature is present strict = [] # For use with --no-default-features @@ -39,7 +43,7 @@ name = "is_enabled" bench = false name = "dump-features" path = "src/bin/dump-features.rs" -required-features = ["async-std", "surf-client"] +required-features = ["surf-client"] [dependencies] anyhow = "1.0.44" @@ -68,14 +72,20 @@ version = "0.4.19" default-features = false features = ["json"] optional = true -version = "0.11.10" +version = "0.12" + +[dependencies.reqwest-11] +default-features = false +features = ["json"] +optional = true +package = "reqwest" +version = "0.11" [dependencies.serde] features = ["derive"] version = "1.0" [dev-dependencies] -# async-std = "1.10.0" criterion = "0.3.5" # Remember to update audit.toml futures = "0.3.17" maplit = "1.0.2" diff --git a/README.md b/README.md index 1093f49..ee81016 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,9 @@ surf or reqwest support is built in, or any async HTTP client can be provided by the user if they implement the thin trait used to abstract over the actual client. -Examples with async-std and tokio are in the examples/ in the source -tree. +Examples with async-std (feature 'surf-client') and tokio (feature +'reqwest-client') are in the examples/ in the source tree. See the API docs for +more feature information. To use it in a sync program, run an async executor and `block_on()` the relevant calls. As the client specification requires sending background metrics to the @@ -69,7 +70,7 @@ enable_string_features | N/A | By default the Rust SDK requires you to define an ## Status -Core Unleash API features work, with Rust 1.59 or above. The MSRV for this project is weakly enforced: when a hard dependency raises its version, so will the minimum version tested against, but if older rust versions work for a user, that is not prevented. `time` in particular is known to enforce a 6-month compiler age, so regular increases with the minimum version tested against are expected. +Core Unleash API features work, with Rust 1.60 or above. The MSRV for this project is weakly enforced: when a hard dependency raises its version, so will the minimum version tested against, but if older rust versions work for a user, that is not prevented. `time` in particular is known to enforce a 6-month compiler age, so regular increases with the minimum version tested against are expected. Unimplemented Unleash specified features: diff --git a/benches/is_enabled.rs b/benches/is_enabled.rs index 3db59de..3256c93 100644 --- a/benches/is_enabled.rs +++ b/benches/is_enabled.rs @@ -222,6 +222,8 @@ fn batch(c: &mut Criterion) { use surf::Client as HttpClient; } else if #[cfg(feature = "reqwest")] { use reqwest::Client as HttpClient; + } else if #[cfg(feature = "reqwest-11")] { + use reqwest_11::Client as HttpClient; } else { compile_error!("Cannot run test suite without a client enabled"); } @@ -423,6 +425,8 @@ fn single_call(c: &mut Criterion) { use surf::Client as HttpClient; } else if #[cfg(feature = "reqwest")] { use reqwest::Client as HttpClient; + } else if #[cfg(feature = "reqwest-11")] { + use reqwest_11::Client as HttpClient; } else { compile_error!("Cannot run test suite without a client enabled"); } diff --git a/examples/threads.rs b/examples/threads.rs index 00c0b30..b8a93fd 100644 --- a/examples/threads.rs +++ b/examples/threads.rs @@ -38,6 +38,10 @@ fn main() -> Result<(), Box> { use reqwest::Client as HttpClient; use tokio::runtime::Runtime; let rt = Arc::new(Runtime::new().unwrap()); + } else if #[cfg(feature = "reqwest-11")] { + use reqwest_11::Client as HttpClient; + use tokio::runtime::Runtime; + let rt = Arc::new(Runtime::new().unwrap()); } else { compile_error!("Cannot run test suite without a client enabled"); } diff --git a/src/client.rs b/src/client.rs index e63679f..d9e8c77 100644 --- a/src/client.rs +++ b/src/client.rs @@ -925,6 +925,8 @@ mod tests { use surf::Client as HttpClient; } else if #[cfg(feature = "reqwest")] { use reqwest::Client as HttpClient; + } else if #[cfg(feature = "reqwest-11")] { + use reqwest_11::Client as HttpClient; } else { compile_error!("Cannot run test suite without a client enabled"); } diff --git a/src/http.rs b/src/http.rs index 635f348..5582c94 100644 --- a/src/http.rs +++ b/src/http.rs @@ -3,6 +3,8 @@ #[cfg(feature = "reqwest")] mod reqwest; +#[cfg(feature = "reqwest-11")] +mod reqwest_11; mod shim; #[cfg(feature = "surf")] mod surf; diff --git a/src/http/reqwest_11.rs b/src/http/reqwest_11.rs new file mode 100644 index 0000000..96191a8 --- /dev/null +++ b/src/http/reqwest_11.rs @@ -0,0 +1,48 @@ +//! Shim reqwest into an unleash HTTP client + +// Copyright 2022 Cognite AS + +use async_trait::async_trait; +use serde::{de::DeserializeOwned, Serialize}; + +use super::HttpClient; + +#[async_trait] +impl HttpClient for reqwest_11::Client { + type HeaderName = reqwest_11::header::HeaderName; + type Error = reqwest_11::Error; + type RequestBuilder = reqwest_11::RequestBuilder; + + fn build_header(name: &'static str) -> Result { + Ok(Self::HeaderName::from_static(name)) + } + + fn get(&self, uri: &str) -> Self::RequestBuilder { + self.get(uri) + } + + fn post(&self, uri: &str) -> Self::RequestBuilder { + self.post(uri) + } + + fn header( + builder: Self::RequestBuilder, + key: &Self::HeaderName, + value: &str, + ) -> Self::RequestBuilder { + builder.header(key.clone(), value) + } + + async fn get_json(req: Self::RequestBuilder) -> Result { + req.send().await?.json::().await + } + + async fn post_json( + req: Self::RequestBuilder, + content: &T, + ) -> Result { + let req = req.json(content); + let res = req.send().await?; + Ok(res.status().is_success()) + } +} diff --git a/src/lib.rs b/src/lib.rs index cf99657..9ad807f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,7 +17,7 @@ strategy memoization function. ```no_run # mod i { # cfg_if::cfg_if!{ -# if #[cfg(not(feature = "surf"))] { +# if #[cfg(not(feature = "surf-client"))] { # pub fn main() -> Result<(), Box> { # Ok(()) # } @@ -99,13 +99,17 @@ fn main() -> Result<(), Box> { * **backtrace** - Enable backtrace feature in anyhow (nightly only) * **default** - - The default feature enables the async-std/surf feature. + By default no features are enabled. * **functional** - Only relevant to developers: enables the functional test suite. * **reqwest-client** - Enables reqwest with OpenSSL TLS support +* **reqwest-client-11** - + Enables reqwest 0.11 with OpenSSL TLS support * **reqwest-client-rustls** - Enables reqwest with RusTLS support +* **reqwest-client-11-rustls** - + Enables reqwest 0.11 with RusTLS support * **strict** - Turn unexpected fields in API responses into errors * **surf-client** - @@ -159,6 +163,8 @@ pub mod prelude { pub use surf::Client as DefaultClient; } else if #[cfg(feature = "reqwest")] { pub use reqwest::Client as DefaultClient; + } else if #[cfg(feature = "reqwest-11")] { + pub use reqwest_11::Client as DefaultClient; } } } diff --git a/tests/clientspec.rs b/tests/clientspec.rs index b3ca47b..21e82b0 100644 --- a/tests/clientspec.rs +++ b/tests/clientspec.rs @@ -47,7 +47,7 @@ mod tests { } _ => false, }, - None => other.payload.get("type").is_none() && other.payload.get("value").is_none(), + None => !other.payload.contains_key("type") && !other.payload.contains_key("value"), }; self.enabled == other.enabled && self._name == other.name && payload_matches } @@ -92,6 +92,8 @@ mod tests { use surf::Client as HttpClient; } else if #[cfg(feature = "reqwest")] { use reqwest::Client as HttpClient; + } else if #[cfg(feature = "reqwest-11")] { + use reqwest_11::Client as HttpClient; } else { compile_error!("Cannot run test suite without a client enabled"); } diff --git a/tests/functional.rs b/tests/functional.rs index 4c08665..947a7b3 100644 --- a/tests/functional.rs +++ b/tests/functional.rs @@ -58,7 +58,7 @@ mod tests { } } - #[cfg(feature = "reqwest")] + #[cfg(or(feature = "reqwest", feature = "reqwest-11"))] struct TokioJoinHandle { inner: tokio::task::JoinHandle<()>, } @@ -80,9 +80,9 @@ mod tests { } } - #[cfg(feature = "reqwest")] + #[cfg(or(feature = "reqwest", feature = "reqwest-11"))] struct TokioAsync; - #[cfg(feature = "reqwest")] + #[cfg(or(feature = "reqwest", feature = "reqwest-11"))] #[async_trait] impl AsyncImpl for TokioAsync { type JoinHandle = TokioJoinHandle; @@ -142,6 +142,11 @@ mod tests { async fn test_smoke_async_reqwest() { test_smoke_async::().await.unwrap(); } + #[cfg(feature = "reqwest-11")] + #[tokio::test] + async fn test_smoke_async_reqwest() { + test_smoke_async::().await.unwrap(); + } async fn test_smoke_threaded( ) -> Result<(), Box> @@ -204,4 +209,11 @@ mod tests { .await .unwrap(); } + #[cfg(feature = "reqwest-11")] + #[tokio::test] + async fn test_smoke_threaded_reqwest() { + test_smoke_threaded::() + .await + .unwrap(); + } }