diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 72e91fdbd..81e0bf096 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -61,7 +61,7 @@ jobs: - name: Install Node and npm uses: actions/setup-node@v2 with: - node-version: '14' + node-version: '16' - name: Install Rust if: matrix.rust diff --git a/CHANGELOG.md b/CHANGELOG.md index 5873b1390..1b85fc954 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,48 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## 🛠 Maintenance ## 📚 Documentation --> +# [0.1.8] 2021-07-07 + +## 🚀 Features + +- **Adds _preview_ support for `@tag` and `@inaccessible` directives - [EverlastingBugstopper], [pull/631]** + + **Preview** support for composing subgraphs with `@tag` and/or `@inaccessible` core features using `rover supergraph compose`. Note that `@apollo/gateway >= 0.33` is required when using **preview** support for these core features. + + [EverlastingBugstopper]: https://github.com/EverlastingBugstopper + [pull/631]: https://github.com/apollographql/rover/pull/631 + +- **Auto-decode gzipped responses - [EverlastingBugstopper], [pull/650]** + + If your GraphQL server responds with an introspection response compressed with brotli, it will now be decoded automatically instead of failing the command. + + [EverlastingBugstopper]: https://github.com/EverlastingBugstopper + [pull/650]: https://github.com/apollographql/rover/pull/650 + +## 🐛 Fixes + +- **Use built-in root certificates and re-use HTTP connection pool - [EverlastingBugstopper], [issue/645] [pull/649]** + + Rover now uses local CA Certificates along with your operating system's native TLS implementation instead of the Rust-based WebPKI implementation. + + [EverlastingBugstopper]: https://github.com/EverlastingBugstopper + [pull/649]: https://github.com/apollographql/rover/pull/649 + [issue/645]: https://github.com/apollographql/rover/issues/645 + +## 🛠 Maintenance + +- **Re-use HTTP connection pool - [EverlastingBugstopper], [pull/650]** + + Rover will now create and reuse the same HTTP connection pool for subsequent requests, which should slightly improve performance. + + [EverlastingBugstopper]: https://github.com/EverlastingBugstopper + [pull/650]: https://github.com/apollographql/rover/pull/650 + +- **Removes unused dependencies - [EverlastingBugstopper], [pull/651]** + + [EverlastingBugstopper]: https://github.com/EverlastingBugstopper + [pull/651]: https://github.com/apollographql/rover/pull/651 + # [0.1.7] 2021-06-29 ## 🚀 Features diff --git a/Cargo.lock b/Cargo.lock index e8c389322..8aba78530 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,6 +26,21 @@ dependencies = [ "memchr", ] +[[package]] +name = "alloc-no-stdlib" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5192ec435945d87bc2f70992b4d818154b5feede43c09fb7592146374eac90a6" + +[[package]] +name = "alloc-stdlib" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "697ed7edc0f1711de49ce108c541623a0af97c6c60b2f6e2b65229847ac843c2" +dependencies = [ + "alloc-no-stdlib", +] + [[package]] name = "ansi_term" version = "0.11.0" @@ -102,6 +117,7 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443ccbb270374a2b1055fc72da40e1f237809cd6bb0e97e66d264cd138473a6" dependencies = [ + "brotli", "flate2", "futures-core", "memchr", @@ -199,6 +215,27 @@ dependencies = [ "generic-array", ] +[[package]] +name = "brotli" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f29919120f08613aadcd4383764e00526fc9f18b6c0895814faeed0dd78613e" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1052e1c3b8d4d80eb84a8b94f0a1498797b5fb96314c001156a1c761940ef4ec" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + [[package]] name = "bstr" version = "0.2.16" @@ -981,9 +1018,9 @@ dependencies = [ [[package]] name = "harmonizer" -version = "0.3.1" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7044c9959a4c2ad619473992e43ffd7287e7df7d0b651f830f195aa88badad34" +checksum = "d067853682d95c6e1fa666637ec7a99c4b20c2c7935ce90ab59bd69cad2ce7ef" dependencies = [ "anyhow", "deno_core", @@ -1093,21 +1130,6 @@ dependencies = [ "want", ] -[[package]] -name = "hyper-rustls" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f9f7a97316d44c0af9b0301e65010573a853a9fc97046d7331d7f6bc0fd5a64" -dependencies = [ - "futures-util", - "hyper", - "log", - "rustls", - "tokio", - "tokio-rustls", - "webpki", -] - [[package]] name = "hyper-tls" version = "0.5.0" @@ -1861,7 +1883,6 @@ dependencies = [ "http", "http-body", "hyper", - "hyper-rustls", "hyper-tls", "ipnet", "js-sys", @@ -1871,37 +1892,19 @@ dependencies = [ "native-tls", "percent-encoding", "pin-project-lite", - "rustls", "serde", "serde_json", "serde_urlencoded", "tokio", "tokio-native-tls", - "tokio-rustls", "tokio-util", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots", "winreg 0.7.0", ] -[[package]] -name = "ring" -version = "0.16.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" -dependencies = [ - "cc", - "libc", - "once_cell", - "spin", - "untrusted", - "web-sys", - "winapi", -] - [[package]] name = "robot-panic" version = "1.0.3" @@ -1918,7 +1921,7 @@ dependencies = [ [[package]] name = "rover" -version = "0.1.7" +version = "0.1.8" dependencies = [ "ansi_term 0.12.1", "anyhow", @@ -2006,19 +2009,6 @@ version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dead70b0b5e03e9c814bcb6b01e03e68f7c57a80aa48c72ec92152ab3e818d49" -[[package]] -name = "rustls" -version = "0.19.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" -dependencies = [ - "base64", - "log", - "ring", - "sct", - "webpki", -] - [[package]] name = "rusty_v8" version = "0.22.3" @@ -2063,16 +2053,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" -[[package]] -name = "sct" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" -dependencies = [ - "ring", - "untrusted", -] - [[package]] name = "sdl-encoder" version = "0.1.0" @@ -2294,12 +2274,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - [[package]] name = "sputnik" version = "0.0.0" @@ -2579,17 +2553,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "tokio-rustls" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6" -dependencies = [ - "rustls", - "tokio", - "webpki", -] - [[package]] name = "tokio-util" version = "0.6.7" @@ -2770,12 +2733,6 @@ dependencies = [ "void", ] -[[package]] -name = "untrusted" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - [[package]] name = "url" version = "2.2.2" @@ -2943,25 +2900,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "webpki" -version = "0.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "webpki-roots" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940" -dependencies = [ - "webpki", -] - [[package]] name = "which" version = "4.1.0" diff --git a/Cargo.toml b/Cargo.toml index 4e01982d8..d07ea73cc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ license = "MIT" name = "rover" readme = "README.md" repository = "https://github.com/apollographql/rover/" -version = "0.1.7" +version = "0.1.8" resolver = "2" [[bin]] @@ -51,12 +51,13 @@ console = "0.14.0" crossterm = "0.20.0" git-url-parse = "0.3.1" git2 = { version = "0.13.20", default-features = false, features = ["vendored-openssl"] } -harmonizer = { version = "0.3.1", optional = true } +harmonizer = { version = "0.26.0", optional = true } heck = "0.3.3" humantime = "2.1.0" opener = "0.5.0" os_info = "3.0" prettytable-rs = "0.8.0" +reqwest = {version = "0.11.4", default-features = false, features = ["blocking"] } serde = "1.0" serde_json = "1.0" serde_yaml = "0.8" @@ -73,7 +74,7 @@ url = { version = "2.2.2", features = ["serde"] } assert_cmd = "1.0.7" assert_fs = "1.0.3" predicates = "2.0.0" -reqwest = "0.11.4" +reqwest = { version = "0.11.4", default-features = false, features = ["blocking", "native-tls-vendored"] } serial_test = "0.5.0" [build-dependencies] diff --git a/README.md b/README.md index fb69e7c32..1e3f692b5 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ rover graph publish --schema ./path-to-valid-schema test@cats ## Command-line options ```console -Rover 0.1.7 +Rover 0.1.8 Rover - Your Graph Companion Read the getting started guide by running: @@ -118,7 +118,7 @@ To install a specific version of Rover (note the `v` prefixing the version numbe > Note: If you're installing Rover in a CI environment, it's best to target a specific version rather than using the latest URL, since future major breaking changes could affect CI workflows otherwise. ```bash -curl -sSL https://rover.apollo.dev/nix/v0.1.7 | sh +curl -sSL https://rover.apollo.dev/nix/v0.1.8 | sh ``` You will need `curl` installed on your system to run the above installation commands. You can get the latest version from [the curl downloads page](https://curl.se/download.html). @@ -136,7 +136,7 @@ To install a specific version of Rover (note the `v` prefixing the version numbe > Note: If you're installing Rover in a CI environment, it's best to target a specific version rather than using the latest URL, since future major breaking changes could affect CI workflows otherwise. ```bash -iwr 'https://rover.apollo.dev/win/v0.1.7' | iex +iwr 'https://rover.apollo.dev/win/v0.1.8' | iex ``` #### npm installer diff --git a/crates/rover-client/Cargo.toml b/crates/rover-client/Cargo.toml index a471d4c92..1fec5258c 100644 --- a/crates/rover-client/Cargo.toml +++ b/crates/rover-client/Cargo.toml @@ -17,7 +17,7 @@ git-url-parse = "0.3.1" git2 = { version = "0.13.20", default-features = false, features = ["vendored-openssl"] } graphql_client = "0.9" http = "0.2" -reqwest = {version = "0.11", default-features = false, features = ["json", "blocking", "rustls-tls", "gzip"]} +reqwest = { version = "0.11", default-features = false, features = ["blocking", "brotli", "gzip", "json", "native-tls-vendored"] } regex = "1" sdl-encoder = {path = "../sdl-encoder"} semver = "1" @@ -33,5 +33,6 @@ reqwest = {version = "0.11", default-features = false, features = ["json", "bloc uuid = {version = "0.8", features = ["v4"]} [dev-dependencies] + indoc = "1.0.3" pretty_assertions = "0.7.1" diff --git a/crates/rover-client/src/blocking/client.rs b/crates/rover-client/src/blocking/client.rs index f9a472a03..05fcaa78c 100644 --- a/crates/rover-client/src/blocking/client.rs +++ b/crates/rover-client/src/blocking/client.rs @@ -13,20 +13,20 @@ pub(crate) const CLIENT_NAME: &str = "rover-client"; /// Represents a generic GraphQL client for making http requests. pub struct GraphQLClient { - client: ReqwestClient, graphql_endpoint: String, + client: ReqwestClient, } impl GraphQLClient { /// Construct a new [Client] from a `graphql_endpoint`. /// This client is used for generic GraphQL requests, such as introspection. - pub fn new(graphql_endpoint: &str) -> Result { + pub fn new( + graphql_endpoint: &str, + client: ReqwestClient, + ) -> Result { Ok(GraphQLClient { - client: ReqwestClient::builder() - .use_rustls_tls() - .gzip(true) - .build()?, graphql_endpoint: graphql_endpoint.to_string(), + client, }) } diff --git a/crates/rover-client/src/blocking/studio_client.rs b/crates/rover-client/src/blocking/studio_client.rs index 12f23011e..405945f86 100644 --- a/crates/rover-client/src/blocking/studio_client.rs +++ b/crates/rover-client/src/blocking/studio_client.rs @@ -7,7 +7,7 @@ use houston::{Credential, CredentialOrigin}; use graphql_client::GraphQLQuery; use reqwest::header::{HeaderMap, HeaderValue}; -use reqwest::Error as ReqwestError; +use reqwest::{blocking::Client as ReqwestClient, Error as ReqwestError}; /// Represents a client for making GraphQL requests to Apollo Studio. pub struct StudioClient { @@ -23,10 +23,11 @@ impl StudioClient { credential: Credential, graphql_endpoint: &str, version: &str, + client: ReqwestClient, ) -> Result { Ok(StudioClient { credential, - client: GraphQLClient::new(graphql_endpoint)?, + client: GraphQLClient::new(graphql_endpoint, client)?, version: version.to_string(), }) } diff --git a/crates/rover-client/src/releases.rs b/crates/rover-client/src/releases.rs index 306bf9b45..8317c4a11 100644 --- a/crates/rover-client/src/releases.rs +++ b/crates/rover-client/src/releases.rs @@ -6,9 +6,9 @@ pub use semver::Version; const LATEST_RELEASE_URL: &str = "https://github.com/apollographql/rover/releases/latest"; /// Looks up and parses the latest release version -pub fn get_latest_release() -> Result { +pub fn get_latest_release(client: Client) -> Result { // send a request to the latest GitHub release - let response = Client::new().head(LATEST_RELEASE_URL).send()?; + let response = client.head(LATEST_RELEASE_URL).send()?; // this will return a response with a redirect to the latest tagged release let url_path_segments = response diff --git a/crates/rover-client/tests/client.rs b/crates/rover-client/tests/client.rs index 5a8486201..8e56fefe5 100644 --- a/crates/rover-client/tests/client.rs +++ b/crates/rover-client/tests/client.rs @@ -1,7 +1,17 @@ +use reqwest::blocking::Client; const STUDIO_PROD_API_ENDPOINT: &str = "https://graphql.api.apollographql.com/api/graphql"; +pub(crate) fn get_client() -> Client { + Client::builder() + .gzip(true) + .brotli(true) + .build() + .expect("Could not create reqwest Client") +} + #[cfg(test)] mod tests { + use super::*; use houston::{Credential, CredentialOrigin}; use rover_client::blocking::{GraphQLClient, StudioClient}; @@ -9,7 +19,7 @@ mod tests { #[test] fn it_can_build_client() { - assert!(GraphQLClient::new(STUDIO_PROD_API_ENDPOINT).is_ok()); + assert!(GraphQLClient::new(STUDIO_PROD_API_ENDPOINT, get_client()).is_ok(),); } #[test] @@ -20,7 +30,8 @@ mod tests { origin: CredentialOrigin::EnvVar, }, "0.1.0", - STUDIO_PROD_API_ENDPOINT + STUDIO_PROD_API_ENDPOINT, + get_client() ) .is_ok()); } diff --git a/crates/sputnik/Cargo.toml b/crates/sputnik/Cargo.toml index e048dba93..07d8ef198 100644 --- a/crates/sputnik/Cargo.toml +++ b/crates/sputnik/Cargo.toml @@ -8,7 +8,7 @@ edition = "2018" camino = "1.0" ci_info = { version = "0.14", features = ["serde-1"] } git2 = "0.13" -reqwest = { version = "0.11", features = ["blocking"] } +reqwest = { version = "0.11", default-features = false, features = ["blocking"] } semver = { version = "1", features = ["serde"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/crates/sputnik/src/report.rs b/crates/sputnik/src/report.rs index 34bd835e4..a15129f4d 100644 --- a/crates/sputnik/src/report.rs +++ b/crates/sputnik/src/report.rs @@ -1,4 +1,5 @@ use camino::{Utf8Path, Utf8PathBuf}; +use reqwest::blocking::Client; use url::Url; use uuid::Uuid; @@ -38,12 +39,15 @@ pub trait Report { /// returns the globally persistent machine identifier /// and writes it if it does not exist - /// the default implemenation uses self.machine_id_config() + /// the default implementation uses self.machine_id_config() /// as the location the machine identifier is written to. fn machine_id(&self) -> Result { let config_path = self.machine_id_config()?; get_or_write_machine_id(&config_path) } + + /// returns the Client to use when sending telemetry data + fn client(&self) -> Client; } fn get_or_write_machine_id(path: &Utf8PathBuf) -> Result { diff --git a/crates/sputnik/src/session.rs b/crates/sputnik/src/session.rs index 706d2691a..9b7a5f6a1 100644 --- a/crates/sputnik/src/session.rs +++ b/crates/sputnik/src/session.rs @@ -1,6 +1,7 @@ use camino::Utf8PathBuf; use ci_info::types::Vendor as CiVendor; use git2::Repository; +use reqwest::blocking::Client; use reqwest::Url; use semver::Version; use serde::Serialize; @@ -45,6 +46,10 @@ pub struct Session { /// Where the telemetry data is being reported to #[serde(skip_serializing)] reporting_info: ReportingInfo, + + /// The reqwest Client sputnik uses to send telemetry data + #[serde(skip_serializing)] + client: Client, } /// Platform represents the platform the CLI is being run from @@ -82,6 +87,7 @@ impl Session { pub fn new(app: &T) -> Result { let machine_id = app.machine_id()?; let command = app.serialize_command()?; + let client = app.client(); let reporting_info = ReportingInfo { is_telemetry_enabled: app.is_telemetry_enabled()?, endpoint: app.endpoint()?, @@ -114,6 +120,7 @@ impl Session { platform, cli_version, reporting_info, + client, }) } @@ -125,7 +132,7 @@ impl Session { let body = serde_json::to_string(&self)?; tracing::debug!("POSTing to {}", &self.reporting_info.endpoint); tracing::debug!("{}", body); - reqwest::blocking::Client::new() + self.client .post(self.reporting_info.endpoint.clone()) .body(body) .header("User-Agent", &self.reporting_info.user_agent) diff --git a/docs/source/getting-started.md b/docs/source/getting-started.md index be35cda4e..2270f6460 100644 --- a/docs/source/getting-started.md +++ b/docs/source/getting-started.md @@ -20,7 +20,7 @@ To install a specific version of Rover (note the `v` prefixing the version numbe > Note: If you're installing Rover in a CI environment, it's best to target a specific version rather than using the latest URL, since future major breaking changes could affect CI workflows otherwise. ```bash -curl -sSL https://rover.apollo.dev/nix/v0.1.7 | sh +curl -sSL https://rover.apollo.dev/nix/v0.1.8 | sh ``` You will need `curl` installed on your system to run the above installation commands. You can get the latest version from [the curl downloads page](https://curl.se/download.html). @@ -38,7 +38,7 @@ To install a specific version of Rover (note the `v` prefixing the version numbe > Note: If you're installing Rover in a CI environment, it's best to target a specific version rather than using the latest URL, since future major breaking changes could affect CI workflows otherwise. ```bash -iwr 'https://rover.apollo.dev/win/v0.1.7' | iex +iwr 'https://rover.apollo.dev/win/v0.1.8' | iex ``` ### `npm` installer diff --git a/installers/binstall/scripts/nix/install.sh b/installers/binstall/scripts/nix/install.sh index 6969cb7b3..75f18dfb7 100755 --- a/installers/binstall/scripts/nix/install.sh +++ b/installers/binstall/scripts/nix/install.sh @@ -16,7 +16,7 @@ BINARY_DOWNLOAD_PREFIX="https://github.com/apollographql/rover/releases/download # Rover version defined in root cargo.toml # Note: this line is built automatically # in build.rs. Don't touch it! -PACKAGE_VERSION="v0.1.7" +PACKAGE_VERSION="v0.1.8" download_binary_and_run_installer() { downloader --check diff --git a/installers/binstall/scripts/windows/install.ps1 b/installers/binstall/scripts/windows/install.ps1 index 3851b6948..ba28b94dc 100644 --- a/installers/binstall/scripts/windows/install.ps1 +++ b/installers/binstall/scripts/windows/install.ps1 @@ -9,7 +9,7 @@ # version found in Rover's Cargo.toml # Note: this line is built automatically # in build.rs. Don't touch it! -$package_version = 'v0.1.7' +$package_version = 'v0.1.8' function Install-Binary() { $old_erroractionpreference = $ErrorActionPreference diff --git a/installers/npm/package-lock.json b/installers/npm/package-lock.json index 58ba92495..fbb0d7d24 100644 --- a/installers/npm/package-lock.json +++ b/installers/npm/package-lock.json @@ -1,12 +1,12 @@ { "name": "@apollo/rover", - "version": "0.1.7", + "version": "0.1.8", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@apollo/rover", - "version": "0.1.7", + "version": "0.1.8", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/installers/npm/package.json b/installers/npm/package.json index 6d7ef3a6e..21d6766da 100644 --- a/installers/npm/package.json +++ b/installers/npm/package.json @@ -1,6 +1,6 @@ { "name": "@apollo/rover", - "version": "0.1.7", + "version": "0.1.8", "description": "The new Apollo CLI", "main": "index.js", "bin": { diff --git a/src/cli.rs b/src/cli.rs index c67f73b78..67224b47c 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,3 +1,4 @@ +use reqwest::blocking::Client; use serde::Serialize; use structopt::{clap::AppSettings, StructOpt}; @@ -60,6 +61,10 @@ pub struct Rover { #[structopt(skip)] #[serde(skip_serializing)] pub env_store: RoverEnv, + + #[structopt(skip)] + #[serde(skip_serializing)] + client: Client, } impl Rover { @@ -75,7 +80,11 @@ impl Rover { pub(crate) fn get_client_config(&self) -> Result { let override_endpoint = self.env_store.get(RoverEnvKey::RegistryUrl)?; let config = self.get_rover_config()?; - Ok(StudioClientConfig::new(override_endpoint, config)) + Ok(StudioClientConfig::new( + override_endpoint, + config, + self.get_reqwest_client(), + )) } pub(crate) fn get_install_override_path(&self) -> Result> { @@ -98,6 +107,11 @@ impl Rover { tracing::debug!(?git_context); Ok(git_context) } + + pub(crate) fn get_reqwest_client(&self) -> Client { + // we can use clone here freely since `reqwest` uses an `Arc` under the hood + self.client.clone() + } } #[derive(Debug, Serialize, StructOpt)] @@ -143,7 +157,7 @@ impl Rover { } else { let config = self.get_rover_config(); if let Ok(config) = config { - let _ = version::check_for_update(config, false); + let _ = version::check_for_update(config, false, self.get_reqwest_client()); } } @@ -157,7 +171,9 @@ impl Rover { Command::Subgraph(command) => { command.run(self.get_client_config()?, self.get_git_context()?) } - Command::Update(command) => command.run(self.get_rover_config()?), + Command::Update(command) => { + command.run(self.get_rover_config()?, self.get_reqwest_client()) + } Command::Install(command) => command.run(self.get_install_override_path()?), Command::Info(command) => command.run(), Command::Explain(command) => command.run(), diff --git a/src/command/config/whoami.rs b/src/command/config/whoami.rs index 332b2c637..7398f9ceb 100644 --- a/src/command/config/whoami.rs +++ b/src/command/config/whoami.rs @@ -23,7 +23,7 @@ pub struct WhoAmI { impl WhoAmI { pub fn run(&self, client_config: StudioClientConfig) -> Result { - let client = client_config.get_client(&self.profile_name)?; + let client = client_config.get_authenticated_client(&self.profile_name)?; eprintln!("Checking identity of your API key against the registry."); let identity = who_am_i::run(ConfigWhoAmIInput {}, &client)?; diff --git a/src/command/graph/check.rs b/src/command/graph/check.rs index a8bf3b821..2792be9a6 100644 --- a/src/command/graph/check.rs +++ b/src/command/graph/check.rs @@ -54,7 +54,7 @@ impl Check { client_config: StudioClientConfig, git_context: GitContext, ) -> Result { - let client = client_config.get_client(&self.profile_name)?; + let client = client_config.get_authenticated_client(&self.profile_name)?; let proposed_schema = load_schema_from_flag(&self.schema, std::io::stdin())?; eprintln!( diff --git a/src/command/graph/fetch.rs b/src/command/graph/fetch.rs index dd2c21d53..173aa4683 100644 --- a/src/command/graph/fetch.rs +++ b/src/command/graph/fetch.rs @@ -25,7 +25,7 @@ pub struct Fetch { impl Fetch { pub fn run(&self, client_config: StudioClientConfig) -> Result { - let client = client_config.get_client(&self.profile_name)?; + let client = client_config.get_authenticated_client(&self.profile_name)?; let graph_ref = self.graph.to_string(); eprintln!( "Fetching SDL from {} using credentials from the {} profile.", diff --git a/src/command/graph/introspect.rs b/src/command/graph/introspect.rs index e674702dd..991f09e5d 100644 --- a/src/command/graph/introspect.rs +++ b/src/command/graph/introspect.rs @@ -1,4 +1,5 @@ use crate::Result; +use reqwest::blocking::Client; use serde::Serialize; use std::collections::HashMap; use structopt::StructOpt; @@ -30,8 +31,8 @@ pub struct Introspect { } impl Introspect { - pub fn run(&self) -> Result { - let client = GraphQLClient::new(&self.endpoint.to_string())?; + pub fn run(&self, client: Client) -> Result { + let client = GraphQLClient::new(&self.endpoint.to_string(), client)?; // add the flag headers to a hashmap to pass along to rover-client let mut headers = HashMap::new(); diff --git a/src/command/graph/mod.rs b/src/command/graph/mod.rs index e06f7999f..410690612 100644 --- a/src/command/graph/mod.rs +++ b/src/command/graph/mod.rs @@ -44,7 +44,7 @@ impl Graph { Command::Check(command) => command.run(client_config, git_context), Command::Fetch(command) => command.run(client_config), Command::Publish(command) => command.run(client_config, git_context), - Command::Introspect(command) => command.run(), + Command::Introspect(command) => command.run(client_config.get_reqwest_client()), } } } diff --git a/src/command/graph/publish.rs b/src/command/graph/publish.rs index b20e53f34..df7d3514d 100644 --- a/src/command/graph/publish.rs +++ b/src/command/graph/publish.rs @@ -37,7 +37,7 @@ impl Publish { client_config: StudioClientConfig, git_context: GitContext, ) -> Result { - let client = client_config.get_client(&self.profile_name)?; + let client = client_config.get_authenticated_client(&self.profile_name)?; let graph_ref = self.graph.to_string(); eprintln!( "Publishing SDL to {} using credentials from the {} profile.", diff --git a/src/command/subgraph/check.rs b/src/command/subgraph/check.rs index 642268efa..dc8c2fabf 100644 --- a/src/command/subgraph/check.rs +++ b/src/command/subgraph/check.rs @@ -59,7 +59,7 @@ impl Check { client_config: StudioClientConfig, git_context: GitContext, ) -> Result { - let client = client_config.get_client(&self.profile_name)?; + let client = client_config.get_authenticated_client(&self.profile_name)?; let proposed_schema = load_schema_from_flag(&self.schema, std::io::stdin())?; diff --git a/src/command/subgraph/delete.rs b/src/command/subgraph/delete.rs index 675138b9e..de53867cb 100644 --- a/src/command/subgraph/delete.rs +++ b/src/command/subgraph/delete.rs @@ -38,7 +38,7 @@ pub struct Delete { impl Delete { pub fn run(&self, client_config: StudioClientConfig) -> Result { - let client = client_config.get_client(&self.profile_name)?; + let client = client_config.get_authenticated_client(&self.profile_name)?; let graph_ref = self.graph.to_string(); eprintln!( "Checking for composition errors resulting from deleting subgraph {} from {} using credentials from the {} profile.", diff --git a/src/command/subgraph/fetch.rs b/src/command/subgraph/fetch.rs index a5dafbe84..7ddf46857 100644 --- a/src/command/subgraph/fetch.rs +++ b/src/command/subgraph/fetch.rs @@ -30,7 +30,7 @@ pub struct Fetch { impl Fetch { pub fn run(&self, client_config: StudioClientConfig) -> Result { - let client = client_config.get_client(&self.profile_name)?; + let client = client_config.get_authenticated_client(&self.profile_name)?; let graph_ref = self.graph.to_string(); eprintln!( "Fetching SDL from {} (subgraph: {}) using credentials from the {} profile.", diff --git a/src/command/subgraph/introspect.rs b/src/command/subgraph/introspect.rs index d514f5639..bdce9cdea 100644 --- a/src/command/subgraph/introspect.rs +++ b/src/command/subgraph/introspect.rs @@ -1,3 +1,4 @@ +use reqwest::blocking::Client; use serde::Serialize; use std::collections::HashMap; use structopt::StructOpt; @@ -35,8 +36,8 @@ pub struct Introspect { } impl Introspect { - pub fn run(&self) -> Result { - let client = GraphQLClient::new(&self.endpoint.to_string())?; + pub fn run(&self, client: Client) -> Result { + let client = GraphQLClient::new(&self.endpoint.to_string(), client)?; // add the flag headers to a hashmap to pass along to rover-client let mut headers = HashMap::new(); diff --git a/src/command/subgraph/list.rs b/src/command/subgraph/list.rs index 3944a8e51..4bcbc312c 100644 --- a/src/command/subgraph/list.rs +++ b/src/command/subgraph/list.rs @@ -25,7 +25,7 @@ pub struct List { impl List { pub fn run(&self, client_config: StudioClientConfig) -> Result { - let client = client_config.get_client(&self.profile_name)?; + let client = client_config.get_authenticated_client(&self.profile_name)?; eprintln!( "Listing subgraphs for {} using credentials from the {} profile.", diff --git a/src/command/subgraph/mod.rs b/src/command/subgraph/mod.rs index d7a45ae94..ee1ff3704 100644 --- a/src/command/subgraph/mod.rs +++ b/src/command/subgraph/mod.rs @@ -50,7 +50,7 @@ impl Subgraph { ) -> Result { match &self.command { Command::Publish(command) => command.run(client_config, git_context), - Command::Introspect(command) => command.run(), + Command::Introspect(command) => command.run(client_config.get_reqwest_client()), Command::Delete(command) => command.run(client_config), Command::Fetch(command) => command.run(client_config), Command::Check(command) => command.run(client_config, git_context), diff --git a/src/command/subgraph/publish.rs b/src/command/subgraph/publish.rs index bd1e0284f..5ee695359 100644 --- a/src/command/subgraph/publish.rs +++ b/src/command/subgraph/publish.rs @@ -57,7 +57,7 @@ impl Publish { client_config: StudioClientConfig, git_context: GitContext, ) -> Result { - let client = client_config.get_client(&self.profile_name)?; + let client = client_config.get_authenticated_client(&self.profile_name)?; let graph_ref = format!("{}:{}", &self.graph.name, &self.graph.variant); eprintln!( "Publishing SDL to {} (subgraph: {}) using credentials from the {} profile.", diff --git a/src/command/supergraph/compose/do_compose.rs b/src/command/supergraph/compose/do_compose.rs index ba76fd2ec..bd2e3609f 100644 --- a/src/command/supergraph/compose/do_compose.rs +++ b/src/command/supergraph/compose/do_compose.rs @@ -105,7 +105,10 @@ pub(crate) fn get_subgraph_definitions( SchemaSource::SubgraphIntrospection { subgraph_url } => { // given a federated introspection URL, use subgraph introspect to // obtain SDL and add it to subgraph_definition. - let client = GraphQLClient::new(&subgraph_url.to_string())?; + let client = GraphQLClient::new( + &subgraph_url.to_string(), + client_config.get_reqwest_client(), + )?; let introspection_response = introspect::run( SubgraphIntrospectInput { @@ -131,7 +134,7 @@ pub(crate) fn get_subgraph_definitions( } => { // given a graph_ref and subgraph, run subgraph fetch to // obtain SDL and add it to subgraph_definition. - let client = client_config.get_client(&profile_name)?; + let client = client_config.get_authenticated_client(&profile_name)?; let result = fetch::run( SubgraphFetchInput { graph_ref: GraphRef::from_str(graph_ref)?, @@ -162,12 +165,17 @@ mod tests { use assert_fs::TempDir; use houston as houston_config; use houston_config::Config; + use reqwest::blocking::Client; use std::convert::TryFrom; fn get_studio_config() -> StudioClientConfig { let tmp_home = TempDir::new().unwrap(); let tmp_path = Utf8PathBuf::try_from(tmp_home.path().to_path_buf()).unwrap(); - StudioClientConfig::new(None, Config::new(Some(&tmp_path), None).unwrap()) + StudioClientConfig::new( + None, + Config::new(Some(&tmp_path), None).unwrap(), + Client::new(), + ) } #[test] diff --git a/src/command/supergraph/fetch.rs b/src/command/supergraph/fetch.rs index 97937af1e..5c4487edc 100644 --- a/src/command/supergraph/fetch.rs +++ b/src/command/supergraph/fetch.rs @@ -24,7 +24,7 @@ pub struct Fetch { impl Fetch { pub fn run(&self, client_config: StudioClientConfig) -> Result { - let client = client_config.get_client(&self.profile_name)?; + let client = client_config.get_authenticated_client(&self.profile_name)?; let graph_ref = self.graph.to_string(); eprintln!( "Fetching supergraph SDL from {} using credentials from the {} profile.", diff --git a/src/command/update/check.rs b/src/command/update/check.rs index 13c745bc7..f4fe31ebd 100644 --- a/src/command/update/check.rs +++ b/src/command/update/check.rs @@ -1,3 +1,4 @@ +use reqwest::blocking::Client; use serde::Serialize; use structopt::StructOpt; @@ -12,8 +13,8 @@ pub struct Check { } impl Check { - pub fn run(&self, config: config::Config) -> Result { - version::check_for_update(config, true)?; + pub fn run(&self, config: config::Config, client: Client) -> Result { + version::check_for_update(config, true, client)?; Ok(RoverStdout::None) } } diff --git a/src/command/update/mod.rs b/src/command/update/mod.rs index 3b95446a1..9296ba172 100644 --- a/src/command/update/mod.rs +++ b/src/command/update/mod.rs @@ -1,5 +1,6 @@ mod check; +use reqwest::blocking::Client; use serde::Serialize; use structopt::StructOpt; @@ -21,9 +22,9 @@ pub enum Command { } impl Update { - pub fn run(&self, config: config::Config) -> Result { + pub fn run(&self, config: config::Config, client: Client) -> Result { match &self.command { - Command::Check(command) => command.run(config), + Command::Check(command) => command.run(config, client), } } } diff --git a/src/utils/client.rs b/src/utils/client.rs index e511c747d..96b7d2633 100644 --- a/src/utils/client.rs +++ b/src/utils/client.rs @@ -2,19 +2,25 @@ use crate::Result; use crate::PKG_VERSION; use houston as config; +use reqwest::blocking::Client; use rover_client::blocking::StudioClient; /// the Apollo graph registry's production API endpoint const STUDIO_PROD_API_ENDPOINT: &str = "https://graphql.api.apollographql.com/api/graphql"; pub struct StudioClientConfig { + pub(crate) config: config::Config, + client: Client, uri: String, - pub config: config::Config, version: String, } impl StudioClientConfig { - pub fn new(override_endpoint: Option, config: config::Config) -> StudioClientConfig { + pub fn new( + override_endpoint: Option, + config: config::Config, + client: Client, + ) -> StudioClientConfig { let version = if cfg!(debug_assertions) { format!("{} (dev)", PKG_VERSION) } else { @@ -25,11 +31,22 @@ impl StudioClientConfig { uri: override_endpoint.unwrap_or_else(|| STUDIO_PROD_API_ENDPOINT.to_string()), config, version, + client, } } - pub fn get_client(&self, profile_name: &str) -> Result { + pub(crate) fn get_reqwest_client(&self) -> Client { + // we can use clone here freely since `reqwest` uses an `Arc` under the hood + self.client.clone() + } + + pub fn get_authenticated_client(&self, profile_name: &str) -> Result { let credential = config::Profile::get_credential(profile_name, &self.config)?; - Ok(StudioClient::new(credential, &self.uri, &self.version)?) + Ok(StudioClient::new( + credential, + &self.uri, + &self.version, + self.get_reqwest_client(), + )?) } } diff --git a/src/utils/telemetry.rs b/src/utils/telemetry.rs index a03aef807..3882364d5 100644 --- a/src/utils/telemetry.rs +++ b/src/utils/telemetry.rs @@ -1,4 +1,5 @@ use camino::Utf8PathBuf; +use reqwest::blocking::Client; use url::Url; use crate::utils::env::RoverEnvKey; @@ -115,6 +116,10 @@ impl Report for Rover { .map_err(|_| SputnikError::ConfigError)?; Ok(config.home.join("machine.txt")) } + + fn client(&self) -> Client { + self.get_reqwest_client() + } } #[cfg(test)] diff --git a/src/utils/version.rs b/src/utils/version.rs index 8076e81ce..9153c0b91 100644 --- a/src/utils/version.rs +++ b/src/utils/version.rs @@ -3,6 +3,7 @@ use std::{fs, time::SystemTime}; use ansi_term::Colour::{Cyan, Yellow}; use billboard::{Alignment, Billboard}; use camino::Utf8PathBuf; +use reqwest::blocking::Client; use crate::{Result, PKG_VERSION}; use houston as config; @@ -17,7 +18,7 @@ const ONE_DAY: u64 = ONE_HOUR * 24; /// check for newer versions, even if we recently checked for updates. /// /// If `force` is not passed, we check for updates every day at most -pub fn check_for_update(config: config::Config, force: bool) -> Result<()> { +pub fn check_for_update(config: config::Config, force: bool, client: Client) -> Result<()> { let version_file = config.home.join("version.toml"); let current_time = SystemTime::now(); // if we don't end up checking, we don't want to overwrite the last checked time @@ -27,7 +28,7 @@ pub fn check_for_update(config: config::Config, force: bool) -> Result<()> { let last_checked_time = get_last_checked_time_from_disk(&version_file); if force || last_checked_time.is_none() { - do_update_check(&mut checked, force)?; + do_update_check(&mut checked, force, client)?; } else if let Some(last_checked_time) = last_checked_time { let time_since_check = current_time.duration_since(last_checked_time)?.as_secs(); tracing::trace!( @@ -36,7 +37,7 @@ pub fn check_for_update(config: config::Config, force: bool) -> Result<()> { ); if time_since_check > ONE_DAY { - do_update_check(&mut checked, force)?; + do_update_check(&mut checked, force, client)?; } } @@ -48,8 +49,12 @@ pub fn check_for_update(config: config::Config, force: bool) -> Result<()> { Ok(()) } -fn do_update_check(checked: &mut bool, should_output_if_updated: bool) -> Result<()> { - let latest_version = get_latest_release()?; +fn do_update_check( + checked: &mut bool, + should_output_if_updated: bool, + client: Client, +) -> Result<()> { + let latest_version = get_latest_release(client)?; let pretty_latest = Cyan.normal().paint(format!("v{}", latest_version)); if latest_version > Version::parse(PKG_VERSION)? { let message = format!(