From e29b87be2395c2d48da30cf348f63c9a57c831e7 Mon Sep 17 00:00:00 2001 From: Avery Harnish Date: Tue, 6 Jul 2021 15:00:31 -0500 Subject: [PATCH] chore: begin adding shared types and consolidate check operations (#652) --- ARCHITECTURE.md | 30 +++-- crates/rover-client/src/error.rs | 19 ++- crates/rover-client/src/lib.rs | 4 +- .../config/is_federated.graphql | 0 .../config/is_federated.rs | 2 +- .../src/{query => operations}/config/mod.rs | 0 .../config/who_am_i/mod.rs | 2 +- .../config/who_am_i/runner.rs} | 4 +- .../config/who_am_i/types.rs | 2 +- .../config/who_am_i/who_am_i_query.graphql | 0 .../graph/check/check_mutation.graphql} | 10 +- .../src/operations/graph/check/mod.rs | 5 + .../graph/check/runner.rs} | 63 +++++----- .../src/operations/graph/check/types.rs | 97 +++++++++++++++ .../{query => operations}/graph/fetch.graphql | 0 .../src/{query => operations}/graph/fetch.rs | 2 +- .../graph/introspect/fixtures/interfaces.json | 0 .../graph/introspect/fixtures/simple.json | 0 .../graph/introspect/fixtures/swapi.json | 0 .../graph/introspect/introspect_query.graphql | 0 .../introspect/introspect_schema.graphql | 0 .../graph/introspect/mod.rs | 4 +- .../graph/introspect/runner.rs} | 6 +- .../graph/introspect/schema.rs | 10 +- .../graph/introspect/types.rs | 2 +- .../src/{query => operations}/graph/mod.rs | 0 .../graph/publish.graphql | 0 .../{query => operations}/graph/publish.rs | 2 +- .../src/{query => operations}/mod.rs | 0 .../subgraph/check/check_mutation.graphql} | 2 +- .../src/operations/subgraph/check/mod.rs | 3 + .../subgraph/check/runner.rs} | 31 +++-- .../src/operations/subgraph/check/types.rs | 76 ++++++++++++ .../subgraph/delete/delete_mutation.graphql | 0 .../subgraph/delete/mod.rs | 4 +- .../subgraph/delete/runner.rs} | 4 +- .../subgraph/delete/types.rs | 2 +- .../subgraph/fetch/fetch_query.graphql | 0 .../subgraph/fetch/mod.rs | 2 +- .../subgraph/fetch/runner.rs} | 2 +- .../subgraph/fetch/types.rs | 2 +- .../introspect/introspect_query.graphql | 0 .../introspect/introspect_schema.graphql | 0 .../subgraph/introspect/mod.rs | 4 +- .../subgraph/introspect/runner.rs} | 6 +- .../subgraph/introspect/types.rs | 2 +- .../subgraph/list/list_query.graphql | 0 .../subgraph/list/mod.rs | 4 +- .../subgraph/list/runner.rs} | 4 +- .../subgraph/list/types.rs | 2 +- .../src/{query => operations}/subgraph/mod.rs | 0 .../subgraph/publish/mod.rs | 2 +- .../subgraph/publish/publish_mutation.graphql | 0 .../subgraph/publish/runner.rs} | 4 +- .../subgraph/publish/types.rs | 4 +- .../supergraph/fetch.graphql | 0 .../{query => operations}/supergraph/fetch.rs | 2 +- .../{query => operations}/supergraph/mod.rs | 0 .../src/query/subgraph/check/mod.rs | 3 - .../src/query/subgraph/check/types.rs | 114 ------------------ .../rover-client/src/shared/check_response.rs | 104 ++++++++++++++++ .../{utils/git.rs => shared/git_context.rs} | 15 +-- crates/rover-client/src/shared/mod.rs | 5 + crates/rover-client/src/utils/mod.rs | 3 - src/cli.rs | 2 +- src/command/config/whoami.rs | 4 +- src/command/graph/check.rs | 85 +++---------- src/command/graph/fetch.rs | 2 +- src/command/graph/introspect.rs | 2 +- src/command/graph/mod.rs | 2 +- src/command/graph/publish.rs | 4 +- src/command/mod.rs | 3 +- src/command/output.rs | 67 +++++----- src/command/subgraph/check.rs | 10 +- src/command/subgraph/delete.rs | 4 +- src/command/subgraph/fetch.rs | 4 +- src/command/subgraph/introspect.rs | 2 +- src/command/subgraph/list.rs | 2 +- src/command/subgraph/mod.rs | 2 +- src/command/subgraph/publish.rs | 8 +- src/command/supergraph/compose/do_compose.rs | 8 +- src/command/supergraph/fetch.rs | 2 +- src/error/metadata/code.rs | 6 +- src/error/metadata/codes/E030.md | 1 + src/error/metadata/mod.rs | 11 +- src/error/metadata/suggestion.rs | 6 +- 86 files changed, 531 insertions(+), 376 deletions(-) rename crates/rover-client/src/{query => operations}/config/is_federated.graphql (100%) rename crates/rover-client/src/{query => operations}/config/is_federated.rs (96%) rename crates/rover-client/src/{query => operations}/config/mod.rs (100%) rename crates/rover-client/src/{query => operations}/config/who_am_i/mod.rs (76%) rename crates/rover-client/src/{query/config/who_am_i/query_runner.rs => operations/config/who_am_i/runner.rs} (97%) rename crates/rover-client/src/{query => operations}/config/who_am_i/types.rs (93%) rename crates/rover-client/src/{query => operations}/config/who_am_i/who_am_i_query.graphql (100%) rename crates/rover-client/src/{query/graph/check.graphql => operations/graph/check/check_mutation.graphql} (80%) create mode 100644 crates/rover-client/src/operations/graph/check/mod.rs rename crates/rover-client/src/{query/graph/check.rs => operations/graph/check/runner.rs} (52%) create mode 100644 crates/rover-client/src/operations/graph/check/types.rs rename crates/rover-client/src/{query => operations}/graph/fetch.graphql (100%) rename crates/rover-client/src/{query => operations}/graph/fetch.rs (98%) rename crates/rover-client/src/{query => operations}/graph/introspect/fixtures/interfaces.json (100%) rename crates/rover-client/src/{query => operations}/graph/introspect/fixtures/simple.json (100%) rename crates/rover-client/src/{query => operations}/graph/introspect/fixtures/swapi.json (100%) rename crates/rover-client/src/{query => operations}/graph/introspect/introspect_query.graphql (100%) rename crates/rover-client/src/{query => operations}/graph/introspect/introspect_schema.graphql (100%) rename crates/rover-client/src/{query => operations}/graph/introspect/mod.rs (71%) rename crates/rover-client/src/{query/graph/introspect/query_runner.rs => operations/graph/introspect/runner.rs} (85%) rename crates/rover-client/src/{query => operations}/graph/introspect/schema.rs (99%) rename crates/rover-client/src/{query => operations}/graph/introspect/types.rs (87%) rename crates/rover-client/src/{query => operations}/graph/mod.rs (100%) rename crates/rover-client/src/{query => operations}/graph/publish.graphql (100%) rename crates/rover-client/src/{query => operations}/graph/publish.rs (99%) rename crates/rover-client/src/{query => operations}/mod.rs (100%) rename crates/rover-client/src/{query/subgraph/check/check_query.graphql => operations/subgraph/check/check_mutation.graphql} (96%) create mode 100644 crates/rover-client/src/operations/subgraph/check/mod.rs rename crates/rover-client/src/{query/subgraph/check/query_runner.rs => operations/subgraph/check/runner.rs} (80%) create mode 100644 crates/rover-client/src/operations/subgraph/check/types.rs rename crates/rover-client/src/{query => operations}/subgraph/delete/delete_mutation.graphql (100%) rename crates/rover-client/src/{query => operations}/subgraph/delete/mod.rs (62%) rename crates/rover-client/src/{query/subgraph/delete/mutation_runner.rs => operations/subgraph/delete/runner.rs} (97%) rename crates/rover-client/src/{query => operations}/subgraph/delete/types.rs (94%) rename crates/rover-client/src/{query => operations}/subgraph/fetch/fetch_query.graphql (100%) rename crates/rover-client/src/{query => operations}/subgraph/fetch/mod.rs (78%) rename crates/rover-client/src/{query/subgraph/fetch/query_runner.rs => operations/subgraph/fetch/runner.rs} (98%) rename crates/rover-client/src/{query => operations}/subgraph/fetch/types.rs (96%) rename crates/rover-client/src/{query => operations}/subgraph/introspect/introspect_query.graphql (100%) rename crates/rover-client/src/{query => operations}/subgraph/introspect/introspect_schema.graphql (100%) rename crates/rover-client/src/{query => operations}/subgraph/introspect/mod.rs (62%) rename crates/rover-client/src/{query/subgraph/introspect/query_runner.rs => operations/subgraph/introspect/runner.rs} (85%) rename crates/rover-client/src/{query => operations}/subgraph/introspect/types.rs (86%) rename crates/rover-client/src/{query => operations}/subgraph/list/list_query.graphql (100%) rename crates/rover-client/src/{query => operations}/subgraph/list/mod.rs (64%) rename crates/rover-client/src/{query/subgraph/list/query_runner.rs => operations/subgraph/list/runner.rs} (98%) rename crates/rover-client/src/{query => operations}/subgraph/list/types.rs (94%) rename crates/rover-client/src/{query => operations}/subgraph/mod.rs (100%) rename crates/rover-client/src/{query => operations}/subgraph/publish/mod.rs (77%) rename crates/rover-client/src/{query => operations}/subgraph/publish/publish_mutation.graphql (100%) rename crates/rover-client/src/{query/subgraph/publish/mutation_runner.rs => operations/subgraph/publish/runner.rs} (97%) rename crates/rover-client/src/{query => operations}/subgraph/publish/types.rs (95%) rename crates/rover-client/src/{query => operations}/supergraph/fetch.graphql (100%) rename crates/rover-client/src/{query => operations}/supergraph/fetch.rs (99%) rename crates/rover-client/src/{query => operations}/supergraph/mod.rs (100%) delete mode 100644 crates/rover-client/src/query/subgraph/check/mod.rs delete mode 100644 crates/rover-client/src/query/subgraph/check/types.rs create mode 100644 crates/rover-client/src/shared/check_response.rs rename crates/rover-client/src/{utils/git.rs => shared/git_context.rs} (95%) create mode 100644 crates/rover-client/src/shared/mod.rs delete mode 100644 crates/rover-client/src/utils/mod.rs create mode 100644 src/error/metadata/codes/E030.md diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 2932cfe2b..d81252fce 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -228,13 +228,13 @@ For more information try --help ##### Setting up a command to work with `rover-client` -Most of Rover's commands make requests to Apollo Studio's API. Rather than handling the request logic in the repository's main package, Rover is structured so that this logic lives in `crates/rover-client`. This is helpful for separation of concerns and testing. +Most of Rover's commands make requests to Apollo Studio's API, or to another GraphQL API. Rather than handling the request logic in the repository's main package, Rover is structured so that this logic lives in `crates/rover-client`. This is helpful for separation of concerns and testing. To access functionality from `rover-client` in our `rover graph hello` command, we'll need to pass down a client from the entry to our command in `src/command/graph/mod.rs`. You can do this by changing the `Command::Hello(command) => command.run(),` line to `Command::Hello(command) => command.run(client_config),`. -Then you'll need to change `Hello::run` to accept a `client_config: StudioClientConfig` parameter in `src/command/graph/hello.rs`, and add a `use crate::utils::client::StudioClientConfig` import statement. Then, at the top of the run function, you can create a `StudioClient` by adding `let client = client_config.get_client(&self.profile_name)?;`. You can see examples of this in the other commands. +Then you'll need to change `Hello::run` to accept a `client_config: StudioClientConfig` parameter in `src/command/graph/hello.rs`, and add a `use crate::utils::client::StudioClientConfig` import statement. Then, at the top of the run function, you can create a `StudioClient` by adding `let client = client_config.get_authenticated_client(&self.profile_name)?;`. You can see examples of this in the other commands. ##### Auto-generated help command @@ -271,15 +271,15 @@ Whenever you create a new command, make sure to add `#[serde(skip_serializing)]` ##### Adding a query to Apollo Studio -The only piece of the `rover-client` crate that we need to be concerned with for now is the `src/query` directory. This is where all the queries to Apollo Studio live. This directory is roughly organized by the command names as well, but there might be some queries in these directories that are used by multiple commands. +The only piece of the `rover-client` crate that we need to be concerned with for now is the `src/operations` directory. This is where all the queries to Apollo Studio live. This directory is roughly organized by the command names as well, but there might be some queries in these directories that are used by multiple commands. -You can see in the `src/query/graph` directory a number of `.rs` files paired with `.graphql` files. The `.graphql` files are the files where the GraphQL operations live, and the matching `.rs` files contain the logic needed to execute those operations. +You can see in the `src/operations/graph` directory a number of `.rs` files paired with `.graphql` files. The `.graphql` files are the files where the GraphQL operations live, and the matching `.rs` files contain the logic needed to execute those operations. ##### Writing a GraphQL operation For our basic `graph hello` command, we're going to make a request to Apollo Studio that inquires about the existence of a particular graph, and nothing else. For this, we can use the `Query.service` field. -Create a `hello.graphql` file in `crates/rover-client/src/query/graph` and paste the following into it: +Create a `hello.graphql` file in `crates/rover-client/src/operations/graph` and paste the following into it: ```graphql query GraphHello($graphId: ID!) { @@ -295,17 +295,19 @@ This basic GraphQL operation uses a graph's unique ID (which we get from the `Gr This project uses [graphql-client](https://docs.rs/graphql_client/latest/graphql_client/) to generate types for each raw `.graphql` query that we write. -First, create an empty file at `crates/rover-client/src/query/graph/hello.rs`. +First, create an empty directory at `crates/rover-client/src/operations/graph/hello`, and then in that directory, create a `mod.rs` file to initialize the module. -To start compiling this file, we need to export the module in `crates/rover-client/src/query/graph/mod.rs`: +To start compiling this file, we need to export the module in `crates/rover-client/src/operations/graph/mod.rs`: ```rust ... -/// "Graph hello" command execution +/// "graph hello" command execution pub mod hello; ``` -Back in `hello.rs`, we'll import the following types: +Back in our `hello` module, we'll create a `runner.rs`, and add `mod runner` to our `mod.rs` file. + +Then, in `runner.rs`, import the following types: ```rust use crate::blocking::StudioClient; @@ -320,7 +322,7 @@ Then, we'll create a new struct that will have auto-generated types for the `hel // The paths are relative to the directory where your `Cargo.toml` is located. // Both json and the GraphQL schema language are supported as sources for the schema #[graphql( - query_path = "src/query/graph/hello.graphql", + query_path = "src/operations/graph/hello/hello_query.graphql", schema_path = ".schema/schema.graphql", response_derives = "PartialEq, Debug, Serialize, Deserialize", deprecated = "warn" @@ -395,7 +397,13 @@ fn build_response( } ``` -This should get you to the point where you can run `rover graph hello ` and see if and when the last graph was deleted. From here, you should be able to follow the examples of other commands to write out tests for the `build_response` function. This is left as an exercise for the reader. +This should get you to the point where you can run `rover graph hello ` and see if and when the last graph was deleted. From here, you should be able to follow the examples of other commands to write out tests for the `build_response` function. + +##### Clean up the API + +Unfortunately this is not the cleanest API and doesn't match the pattern set by the rest of the commands. Each `rover-client` operation has an input type and an output type, along with a `run` function that takes in a `reqwest::blocking::Client`. + +You'll want to define all of the types scoped to this command in `types.rs`, and re-export them from the top level `hello` module, and nothing else. ##### `RoverStdout` diff --git a/crates/rover-client/src/error.rs b/crates/rover-client/src/error.rs index f2ddd2a61..220cf319e 100644 --- a/crates/rover-client/src/error.rs +++ b/crates/rover-client/src/error.rs @@ -1,7 +1,7 @@ use reqwest::Url; use thiserror::Error; -use crate::query::subgraph::check::types::CompositionError; +use crate::{operations::subgraph::check::types::CompositionError, shared::CheckResponse}; /// RoverClientError represents all possible failures that can occur during a client request. #[derive(Error, Debug)] @@ -132,6 +132,12 @@ pub enum RoverClientError { #[error("Invalid ChangeSeverity.")] InvalidSeverity, + /// While checking the proposed schema, we encountered changes that would break existing operations + // we nest the CheckResponse here because we want to print the entire response even + // if there were failures + #[error("{}", check_response_error_msg(.check_response))] + OperationCheckFailure { check_response: CheckResponse }, + /// This error occurs when a user has a malformed API key #[error( "The API key you provided is malformed. An API key must have three parts separated by a colon." @@ -169,3 +175,14 @@ fn subgraph_composition_error_msg(composition_errors: &[CompositionError]) -> St }); msg } + +fn check_response_error_msg(check_response: &CheckResponse) -> String { + let plural = match check_response.num_failures { + 1 => "", + _ => "s", + }; + format!( + "This operation has encountered {} change{} that would break existing clients.", + check_response.num_failures, plural + ) +} diff --git a/crates/rover-client/src/lib.rs b/crates/rover-client/src/lib.rs index aa15757c7..5c8f3e490 100644 --- a/crates/rover-client/src/lib.rs +++ b/crates/rover-client/src/lib.rs @@ -12,10 +12,10 @@ pub use error::RoverClientError; #[allow(clippy::upper_case_acronyms)] /// Module for actually querying studio -pub mod query; +pub mod operations; /// Module for getting release info pub mod releases; /// Module for shared functionality -pub mod utils; +pub mod shared; diff --git a/crates/rover-client/src/query/config/is_federated.graphql b/crates/rover-client/src/operations/config/is_federated.graphql similarity index 100% rename from crates/rover-client/src/query/config/is_federated.graphql rename to crates/rover-client/src/operations/config/is_federated.graphql diff --git a/crates/rover-client/src/query/config/is_federated.rs b/crates/rover-client/src/operations/config/is_federated.rs similarity index 96% rename from crates/rover-client/src/query/config/is_federated.rs rename to crates/rover-client/src/operations/config/is_federated.rs index b8fb68fa6..4175e1f2f 100644 --- a/crates/rover-client/src/query/config/is_federated.rs +++ b/crates/rover-client/src/operations/config/is_federated.rs @@ -7,7 +7,7 @@ use graphql_client::*; // The paths are relative to the directory where your `Cargo.toml` is located. // Both json and the GraphQL schema language are supported as sources for the schema #[graphql( - query_path = "src/query/config/is_federated.graphql", + query_path = "src/operations/config/is_federated.graphql", schema_path = ".schema/schema.graphql", response_derives = "PartialEq, Debug, Serialize, Deserialize", deprecated = "warn" diff --git a/crates/rover-client/src/query/config/mod.rs b/crates/rover-client/src/operations/config/mod.rs similarity index 100% rename from crates/rover-client/src/query/config/mod.rs rename to crates/rover-client/src/operations/config/mod.rs diff --git a/crates/rover-client/src/query/config/who_am_i/mod.rs b/crates/rover-client/src/operations/config/who_am_i/mod.rs similarity index 76% rename from crates/rover-client/src/query/config/who_am_i/mod.rs rename to crates/rover-client/src/operations/config/who_am_i/mod.rs index 30535495a..9e6871fdd 100644 --- a/crates/rover-client/src/query/config/who_am_i/mod.rs +++ b/crates/rover-client/src/operations/config/who_am_i/mod.rs @@ -1,4 +1,4 @@ mod types; -pub mod query_runner; +pub mod runner; pub use types::{Actor, ConfigWhoAmIInput, RegistryIdentity}; diff --git a/crates/rover-client/src/query/config/who_am_i/query_runner.rs b/crates/rover-client/src/operations/config/who_am_i/runner.rs similarity index 97% rename from crates/rover-client/src/query/config/who_am_i/query_runner.rs rename to crates/rover-client/src/operations/config/who_am_i/runner.rs index 0bc7eba7e..d63bf176c 100644 --- a/crates/rover-client/src/query/config/who_am_i/query_runner.rs +++ b/crates/rover-client/src/operations/config/who_am_i/runner.rs @@ -1,5 +1,5 @@ use crate::blocking::StudioClient; -use crate::query::config::who_am_i::{ +use crate::operations::config::who_am_i::{ types::{QueryActorType, QueryResponseData, RegistryIdentity}, Actor, ConfigWhoAmIInput, }; @@ -13,7 +13,7 @@ use graphql_client::*; // The paths are relative to the directory where your `Cargo.toml` is located. // Both json and the GraphQL schema language are supported as sources for the schema #[graphql( - query_path = "src/query/config/who_am_i/who_am_i_query.graphql", + query_path = "src/operations/config/who_am_i/who_am_i_query.graphql", schema_path = ".schema/schema.graphql", response_derives = "PartialEq, Debug, Serialize, Deserialize", deprecated = "warn" diff --git a/crates/rover-client/src/query/config/who_am_i/types.rs b/crates/rover-client/src/operations/config/who_am_i/types.rs similarity index 93% rename from crates/rover-client/src/query/config/who_am_i/types.rs rename to crates/rover-client/src/operations/config/who_am_i/types.rs index c9cfeafa2..d2d3c4e93 100644 --- a/crates/rover-client/src/query/config/who_am_i/types.rs +++ b/crates/rover-client/src/operations/config/who_am_i/types.rs @@ -1,4 +1,4 @@ -use super::query_runner::config_who_am_i_query; +use super::runner::config_who_am_i_query; use houston::CredentialOrigin; diff --git a/crates/rover-client/src/query/config/who_am_i/who_am_i_query.graphql b/crates/rover-client/src/operations/config/who_am_i/who_am_i_query.graphql similarity index 100% rename from crates/rover-client/src/query/config/who_am_i/who_am_i_query.graphql rename to crates/rover-client/src/operations/config/who_am_i/who_am_i_query.graphql diff --git a/crates/rover-client/src/query/graph/check.graphql b/crates/rover-client/src/operations/graph/check/check_mutation.graphql similarity index 80% rename from crates/rover-client/src/query/graph/check.graphql rename to crates/rover-client/src/operations/graph/check/check_mutation.graphql index a891ba7eb..e936d2993 100644 --- a/crates/rover-client/src/query/graph/check.graphql +++ b/crates/rover-client/src/operations/graph/check/check_mutation.graphql @@ -1,8 +1,8 @@ -mutation CheckSchemaQuery( - $graphId: ID! +mutation GraphCheckMutation( + $graph_id: ID! $variant: String - $schema: String - $gitContext: GitContextInput! + $proposed_schema: String + $git_context: GitContextInput! $config: HistoricQueryParameters! ) { service(id: $graphId) { @@ -24,4 +24,4 @@ mutation CheckSchemaQuery( } } } -} \ No newline at end of file +} diff --git a/crates/rover-client/src/operations/graph/check/mod.rs b/crates/rover-client/src/operations/graph/check/mod.rs new file mode 100644 index 000000000..23c23b7e8 --- /dev/null +++ b/crates/rover-client/src/operations/graph/check/mod.rs @@ -0,0 +1,5 @@ +mod runner; +mod types; + +pub use runner::run; +pub use types::GraphCheckInput; diff --git a/crates/rover-client/src/query/graph/check.rs b/crates/rover-client/src/operations/graph/check/runner.rs similarity index 52% rename from crates/rover-client/src/query/graph/check.rs rename to crates/rover-client/src/operations/graph/check/runner.rs index c57dad4ca..190dec094 100644 --- a/crates/rover-client/src/query/graph/check.rs +++ b/crates/rover-client/src/operations/graph/check/runner.rs @@ -1,76 +1,67 @@ use crate::blocking::StudioClient; +use crate::operations::graph::check::types::{ + GraphCheckInput, MutationChangeSeverity, MutationResponseData, +}; +use crate::shared::CheckResponse; use crate::RoverClientError; -use graphql_client::*; -use reqwest::Url; +use graphql_client::*; type Timestamp = String; #[derive(GraphQLQuery)] // The paths are relative to the directory where your `Cargo.toml` is located. // Both json and the GraphQL schema language are supported as sources for the schema #[graphql( - query_path = "src/query/graph/check.graphql", + query_path = "src/operations/graph/check/check_mutation.graphql", schema_path = ".schema/schema.graphql", response_derives = "PartialEq, Debug, Serialize, Deserialize", deprecated = "warn" )] /// This struct is used to generate the module containing `Variables` and /// `ResponseData` structs. -/// Snake case of this name is the mod name. i.e. check_schema_query -pub struct CheckSchemaQuery; +/// Snake case of this name is the mod name. i.e. graph_check_mutation +pub(crate) struct GraphCheckMutation; /// The main function to be used from this module. /// This function takes a proposed schema and validates it against a published /// schema. pub fn run( - variables: check_schema_query::Variables, + input: GraphCheckInput, client: &StudioClient, ) -> Result { - let graph = variables.graph_id.clone(); - let data = client.post::(variables)?; + let graph = input.graph_id.clone(); + let data = client.post::(input.into())?; get_check_response_from_data(data, graph) } -#[derive(Debug)] -pub struct CheckResponse { - pub target_url: Option, - pub number_of_checked_operations: i64, - pub change_severity: check_schema_query::ChangeSeverity, - pub changes: Vec, -} - fn get_check_response_from_data( - data: check_schema_query::ResponseData, + data: MutationResponseData, graph: String, ) -> Result { let service = data.service.ok_or(RoverClientError::NoService { graph })?; - let target_url = get_url(service.check_schema.target_url); + let target_url = service.check_schema.target_url; let diff_to_previous = service.check_schema.diff_to_previous; let number_of_checked_operations = diff_to_previous.number_of_checked_operations.unwrap_or(0); - let change_severity = diff_to_previous.severity; - let changes = diff_to_previous.changes; + let change_severity = diff_to_previous.severity.into(); + let mut changes = Vec::with_capacity(diff_to_previous.changes.len()); + let mut num_failures = 0; + for change in diff_to_previous.changes { + if let MutationChangeSeverity::FAILURE = change.severity { + num_failures += 1; + } + changes.push(change.into()); + } - Ok(CheckResponse { + let check_response = CheckResponse { target_url, number_of_checked_operations, - change_severity, changes, - }) -} + change_severity, + num_failures, + }; -fn get_url(url: Option) -> Option { - match url { - Some(url) => { - let url = Url::parse(&url); - match url { - Ok(url) => Some(url), - // if the API returns an invalid URL, don't put it in the response - Err(_) => None, - } - } - None => None, - } + check_response.check_for_failures() } diff --git a/crates/rover-client/src/operations/graph/check/types.rs b/crates/rover-client/src/operations/graph/check/types.rs new file mode 100644 index 000000000..61c16efac --- /dev/null +++ b/crates/rover-client/src/operations/graph/check/types.rs @@ -0,0 +1,97 @@ +use crate::operations::graph::check::runner::graph_check_mutation; +use crate::shared::{ChangeSeverity, CheckConfig, GitContext, SchemaChange}; + +#[derive(Debug, Clone, PartialEq)] +pub struct GraphCheckInput { + pub graph_id: String, + pub variant: String, + pub proposed_schema: String, + pub git_context: GitContext, + pub config: CheckConfig, +} + +impl From for MutationVariables { + fn from(input: GraphCheckInput) -> Self { + Self { + graph_id: input.graph_id, + variant: Some(input.variant), + proposed_schema: Some(input.proposed_schema), + config: input.config.into(), + git_context: input.git_context.into(), + } + } +} + +type MutationConfig = graph_check_mutation::HistoricQueryParameters; +impl From for MutationConfig { + fn from(input: CheckConfig) -> Self { + Self { + query_count_threshold: input.query_count_threshold, + query_count_threshold_percentage: input.query_count_threshold_percentage, + from: input.validation_period_from, + to: input.validation_period_to, + // we don't support configuring these, but we can't leave them out + excluded_clients: None, + ignored_operations: None, + included_variants: None, + } + } +} + +type MutationVariables = graph_check_mutation::Variables; +pub(crate) type MutationResponseData = graph_check_mutation::ResponseData; + +pub(crate) type MutationChangeSeverity = graph_check_mutation::ChangeSeverity; +impl From for ChangeSeverity { + fn from(severity: MutationChangeSeverity) -> Self { + match severity { + MutationChangeSeverity::NOTICE => ChangeSeverity::PASS, + MutationChangeSeverity::FAILURE => ChangeSeverity::FAIL, + _ => ChangeSeverity::unreachable(), + } + } +} + +impl From for MutationChangeSeverity { + fn from(severity: ChangeSeverity) -> Self { + match severity { + ChangeSeverity::PASS => MutationChangeSeverity::NOTICE, + ChangeSeverity::FAIL => MutationChangeSeverity::FAILURE, + } + } +} + +type MutationSchemaChange = + graph_check_mutation::GraphCheckMutationServiceCheckSchemaDiffToPreviousChanges; +impl From for MutationSchemaChange { + fn from(schema_change: SchemaChange) -> MutationSchemaChange { + MutationSchemaChange { + severity: schema_change.severity.into(), + code: schema_change.code, + description: schema_change.description, + } + } +} + +impl From for SchemaChange { + fn from(schema_change: MutationSchemaChange) -> SchemaChange { + SchemaChange { + severity: schema_change.severity.into(), + code: schema_change.code, + description: schema_change.description, + } + } +} + +type MutationGitContextInput = graph_check_mutation::GitContextInput; +impl From for MutationGitContextInput { + fn from(git_context: GitContext) -> MutationGitContextInput { + MutationGitContextInput { + branch: git_context.branch, + commit: git_context.commit, + committer: git_context.author, + remote_url: git_context.remote_url, + message: None, + } + } +} diff --git a/crates/rover-client/src/query/graph/fetch.graphql b/crates/rover-client/src/operations/graph/fetch.graphql similarity index 100% rename from crates/rover-client/src/query/graph/fetch.graphql rename to crates/rover-client/src/operations/graph/fetch.graphql diff --git a/crates/rover-client/src/query/graph/fetch.rs b/crates/rover-client/src/operations/graph/fetch.rs similarity index 98% rename from crates/rover-client/src/query/graph/fetch.rs rename to crates/rover-client/src/operations/graph/fetch.rs index 2890334e0..3ac6f2aba 100644 --- a/crates/rover-client/src/query/graph/fetch.rs +++ b/crates/rover-client/src/operations/graph/fetch.rs @@ -10,7 +10,7 @@ type GraphQLDocument = String; // The paths are relative to the directory where your `Cargo.toml` is located. // Both json and the GraphQL schema language are supported as sources for the schema #[graphql( - query_path = "src/query/graph/fetch.graphql", + query_path = "src/operations/graph/fetch.graphql", schema_path = ".schema/schema.graphql", response_derives = "PartialEq, Debug, Serialize, Deserialize", deprecated = "warn" diff --git a/crates/rover-client/src/query/graph/introspect/fixtures/interfaces.json b/crates/rover-client/src/operations/graph/introspect/fixtures/interfaces.json similarity index 100% rename from crates/rover-client/src/query/graph/introspect/fixtures/interfaces.json rename to crates/rover-client/src/operations/graph/introspect/fixtures/interfaces.json diff --git a/crates/rover-client/src/query/graph/introspect/fixtures/simple.json b/crates/rover-client/src/operations/graph/introspect/fixtures/simple.json similarity index 100% rename from crates/rover-client/src/query/graph/introspect/fixtures/simple.json rename to crates/rover-client/src/operations/graph/introspect/fixtures/simple.json diff --git a/crates/rover-client/src/query/graph/introspect/fixtures/swapi.json b/crates/rover-client/src/operations/graph/introspect/fixtures/swapi.json similarity index 100% rename from crates/rover-client/src/query/graph/introspect/fixtures/swapi.json rename to crates/rover-client/src/operations/graph/introspect/fixtures/swapi.json diff --git a/crates/rover-client/src/query/graph/introspect/introspect_query.graphql b/crates/rover-client/src/operations/graph/introspect/introspect_query.graphql similarity index 100% rename from crates/rover-client/src/query/graph/introspect/introspect_query.graphql rename to crates/rover-client/src/operations/graph/introspect/introspect_query.graphql diff --git a/crates/rover-client/src/query/graph/introspect/introspect_schema.graphql b/crates/rover-client/src/operations/graph/introspect/introspect_schema.graphql similarity index 100% rename from crates/rover-client/src/query/graph/introspect/introspect_schema.graphql rename to crates/rover-client/src/operations/graph/introspect/introspect_schema.graphql diff --git a/crates/rover-client/src/query/graph/introspect/mod.rs b/crates/rover-client/src/operations/graph/introspect/mod.rs similarity index 71% rename from crates/rover-client/src/query/graph/introspect/mod.rs rename to crates/rover-client/src/operations/graph/introspect/mod.rs index bc5028d54..541b12ff2 100644 --- a/crates/rover-client/src/query/graph/introspect/mod.rs +++ b/crates/rover-client/src/operations/graph/introspect/mod.rs @@ -1,7 +1,7 @@ -mod query_runner; +mod runner; mod schema; mod types; -pub use query_runner::run; +pub use runner::run; pub use schema::Schema; pub use types::{GraphIntrospectInput, GraphIntrospectResponse}; diff --git a/crates/rover-client/src/query/graph/introspect/query_runner.rs b/crates/rover-client/src/operations/graph/introspect/runner.rs similarity index 85% rename from crates/rover-client/src/query/graph/introspect/query_runner.rs rename to crates/rover-client/src/operations/graph/introspect/runner.rs index 8188f18b2..c9516c10d 100644 --- a/crates/rover-client/src/query/graph/introspect/query_runner.rs +++ b/crates/rover-client/src/operations/graph/introspect/runner.rs @@ -1,5 +1,5 @@ use crate::blocking::GraphQLClient; -use crate::query::graph::introspect::{types::*, Schema}; +use crate::operations::graph::introspect::{types::*, Schema}; use crate::RoverClientError; use graphql_client::*; @@ -7,8 +7,8 @@ use std::convert::TryFrom; #[derive(GraphQLQuery)] #[graphql( - query_path = "src/query/graph/introspect/introspect_query.graphql", - schema_path = "src/query/graph/introspect/introspect_schema.graphql", + query_path = "src/operations/graph/introspect/introspect_query.graphql", + schema_path = "src/operations//graph/introspect/introspect_schema.graphql", response_derives = "PartialEq, Debug, Serialize, Deserialize", deprecated = "warn" )] diff --git a/crates/rover-client/src/query/graph/introspect/schema.rs b/crates/rover-client/src/operations/graph/introspect/schema.rs similarity index 99% rename from crates/rover-client/src/query/graph/introspect/schema.rs rename to crates/rover-client/src/operations/graph/introspect/schema.rs index d7f014a73..a6dbbe8f0 100644 --- a/crates/rover-client/src/query/graph/introspect/schema.rs +++ b/crates/rover-client/src/operations/graph/introspect/schema.rs @@ -10,7 +10,7 @@ use sdl_encoder::{ use serde::Deserialize; use std::convert::TryFrom; -use crate::query::graph::introspect::query_runner::graph_introspect_query; +use crate::operations::graph::introspect::runner::graph_introspect_query; type FullTypeField = graph_introspect_query::FullTypeFields; type FullTypeInputField = graph_introspect_query::FullTypeInputFields; @@ -363,11 +363,11 @@ mod tests { use std::convert::TryFrom; use std::fs::File; - use crate::query::graph::introspect::types::QueryResponseData; + use crate::operations::graph::introspect::types::QueryResponseData; #[test] fn it_builds_simple_schema() { - let file = File::open("src/query/graph/introspect/fixtures/simple.json").unwrap(); + let file = File::open("src/operations/graph/introspect/fixtures/simple.json").unwrap(); let res: Response = serde_json::from_reader(file).unwrap(); let data = res.data.unwrap(); @@ -401,7 +401,7 @@ mod tests { #[test] fn it_builds_swapi_schema() { - let file = File::open("src/query/graph/introspect/fixtures/swapi.json").unwrap(); + let file = File::open("src/operations/graph/introspect/fixtures/swapi.json").unwrap(); let res: Response = serde_json::from_reader(file).unwrap(); let data = res.data.unwrap(); @@ -1364,7 +1364,7 @@ mod tests { #[test] fn it_builds_schema_with_interfaces() { - let file = File::open("src/query/graph/introspect/fixtures/interfaces.json").unwrap(); + let file = File::open("src/operations/graph/introspect/fixtures/interfaces.json").unwrap(); let res: Response = serde_json::from_reader(file).unwrap(); let data = res.data.unwrap(); diff --git a/crates/rover-client/src/query/graph/introspect/types.rs b/crates/rover-client/src/operations/graph/introspect/types.rs similarity index 87% rename from crates/rover-client/src/query/graph/introspect/types.rs rename to crates/rover-client/src/operations/graph/introspect/types.rs index aa9089cd5..73fd29965 100644 --- a/crates/rover-client/src/query/graph/introspect/types.rs +++ b/crates/rover-client/src/operations/graph/introspect/types.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use crate::query::graph::introspect::query_runner::graph_introspect_query; +use crate::operations::graph::introspect::runner::graph_introspect_query; pub(crate) type QueryResponseData = graph_introspect_query::ResponseData; pub(crate) type QueryVariables = graph_introspect_query::Variables; diff --git a/crates/rover-client/src/query/graph/mod.rs b/crates/rover-client/src/operations/graph/mod.rs similarity index 100% rename from crates/rover-client/src/query/graph/mod.rs rename to crates/rover-client/src/operations/graph/mod.rs diff --git a/crates/rover-client/src/query/graph/publish.graphql b/crates/rover-client/src/operations/graph/publish.graphql similarity index 100% rename from crates/rover-client/src/query/graph/publish.graphql rename to crates/rover-client/src/operations/graph/publish.graphql diff --git a/crates/rover-client/src/query/graph/publish.rs b/crates/rover-client/src/operations/graph/publish.rs similarity index 99% rename from crates/rover-client/src/query/graph/publish.rs rename to crates/rover-client/src/operations/graph/publish.rs index 3c5955600..d9b8b9787 100644 --- a/crates/rover-client/src/query/graph/publish.rs +++ b/crates/rover-client/src/operations/graph/publish.rs @@ -6,7 +6,7 @@ use graphql_client::*; // The paths are relative to the directory where your `Cargo.toml` is located. // Both json and the GraphQL schema language are supported as sources for the schema #[graphql( - query_path = "src/query/graph/publish.graphql", + query_path = "src/operations/graph/publish.graphql", schema_path = ".schema/schema.graphql", response_derives = "PartialEq, Debug, Serialize, Deserialize", deprecated = "warn" diff --git a/crates/rover-client/src/query/mod.rs b/crates/rover-client/src/operations/mod.rs similarity index 100% rename from crates/rover-client/src/query/mod.rs rename to crates/rover-client/src/operations/mod.rs diff --git a/crates/rover-client/src/query/subgraph/check/check_query.graphql b/crates/rover-client/src/operations/subgraph/check/check_mutation.graphql similarity index 96% rename from crates/rover-client/src/query/subgraph/check/check_query.graphql rename to crates/rover-client/src/operations/subgraph/check/check_mutation.graphql index 4afaecbcc..906dfd831 100644 --- a/crates/rover-client/src/query/subgraph/check/check_query.graphql +++ b/crates/rover-client/src/operations/subgraph/check/check_mutation.graphql @@ -1,4 +1,4 @@ - mutation SubgraphCheckQuery( + mutation SubgraphCheckMutation( $graph_id: ID! $variant: String! $subgraph: String! diff --git a/crates/rover-client/src/operations/subgraph/check/mod.rs b/crates/rover-client/src/operations/subgraph/check/mod.rs new file mode 100644 index 000000000..9734f57b4 --- /dev/null +++ b/crates/rover-client/src/operations/subgraph/check/mod.rs @@ -0,0 +1,3 @@ +pub mod runner; +pub(crate) mod types; +pub use types::SubgraphCheckInput; diff --git a/crates/rover-client/src/query/subgraph/check/query_runner.rs b/crates/rover-client/src/operations/subgraph/check/runner.rs similarity index 80% rename from crates/rover-client/src/query/subgraph/check/query_runner.rs rename to crates/rover-client/src/operations/subgraph/check/runner.rs index b602595ea..94d91bd85 100644 --- a/crates/rover-client/src/query/subgraph/check/query_runner.rs +++ b/crates/rover-client/src/operations/subgraph/check/runner.rs @@ -1,22 +1,25 @@ use super::types::*; use crate::blocking::StudioClient; -use crate::query::config::is_federated; +use crate::operations::{config::is_federated, subgraph::check::types::MutationResponseData}; +use crate::shared::{CheckResponse, SchemaChange}; use crate::RoverClientError; + use graphql_client::*; +type Timestamp = String; #[derive(GraphQLQuery)] // The paths are relative to the directory where your `Cargo.toml` is located. // Both json and the GraphQL schema language are supported as sources for the schema #[graphql( - query_path = "src/query/subgraph/check/check_query.graphql", + query_path = "src/operations/subgraph/check/check_mutation.graphql", schema_path = ".schema/schema.graphql", response_derives = "PartialEq, Debug, Serialize, Deserialize", deprecated = "warn" )] /// This struct is used to generate the module containing `Variables` and /// `ResponseData` structs. -/// Snake case of this name is the mod name. i.e. subgraph_check_query -pub struct SubgraphCheckQuery; +/// Snake case of this name is the mod name. i.e. subgraph_check_mutation +pub(crate) struct SubgraphCheckMutation; /// The main function to be used from this module. /// This function takes a proposed schema and validates it against a published @@ -24,7 +27,7 @@ pub struct SubgraphCheckQuery; pub fn run( input: SubgraphCheckInput, client: &StudioClient, -) -> Result { +) -> Result { let graph = input.graph_id.clone(); // This response is used to check whether or not the current graph is federated. let is_federated = is_federated::run( @@ -41,14 +44,14 @@ pub fn run( }); } let variables = input.into(); - let data = client.post::(variables)?; + let data = client.post::(variables)?; get_check_response_from_data(data, graph) } fn get_check_response_from_data( - data: subgraph_check_query::ResponseData, + data: MutationResponseData, graph_name: String, -) -> Result { +) -> Result { let service = data.service.ok_or(RoverClientError::NoService { graph: graph_name.clone(), })?; @@ -56,7 +59,7 @@ fn get_check_response_from_data( // for some reason this is a `Vec>` // we convert this to just `Vec` because the `None` // errors would be useless. - let query_composition_errors: Vec = service + let query_composition_errors: Vec = service .check_partial_schema .composition_validation_result .errors; @@ -76,7 +79,11 @@ fn get_check_response_from_data( let change_severity = diff_to_previous.severity.into(); let mut changes = Vec::with_capacity(diff_to_previous.changes.len()); + let mut num_failures = 0; for change in diff_to_previous.changes { + if let MutationChangeSeverity::FAILURE = change.severity { + num_failures += 1; + } changes.push(SchemaChange { code: change.code, severity: change.severity.into(), @@ -84,14 +91,14 @@ fn get_check_response_from_data( }); } - let check_result = SubgraphCheckResponse { + let check_response = CheckResponse { + num_failures, target_url: check_schema_result.target_url, number_of_checked_operations, changes, change_severity, }; - - Ok(check_result) + check_response.check_for_failures() } else { let num_failures = query_composition_errors.len(); diff --git a/crates/rover-client/src/operations/subgraph/check/types.rs b/crates/rover-client/src/operations/subgraph/check/types.rs new file mode 100644 index 000000000..2fdfd258a --- /dev/null +++ b/crates/rover-client/src/operations/subgraph/check/types.rs @@ -0,0 +1,76 @@ +use crate::operations::subgraph::check::runner::subgraph_check_mutation; +use crate::shared::{ChangeSeverity, CheckConfig, GitContext}; + +type MutationVariables = subgraph_check_mutation::Variables; + +pub(crate) type MutationResponseData = subgraph_check_mutation::ResponseData; +pub(crate) type MutationCompositionErrors = + subgraph_check_mutation::SubgraphCheckMutationServiceCheckPartialSchemaCompositionValidationResultErrors; + +type MutationSchema = subgraph_check_mutation::PartialSchemaInput; +type MutationConfig = subgraph_check_mutation::HistoricQueryParameters; + +pub(crate) type MutationChangeSeverity = subgraph_check_mutation::ChangeSeverity; +impl From for ChangeSeverity { + fn from(severity: MutationChangeSeverity) -> Self { + match severity { + MutationChangeSeverity::NOTICE => ChangeSeverity::PASS, + MutationChangeSeverity::FAILURE => ChangeSeverity::FAIL, + _ => ChangeSeverity::unreachable(), + } + } +} + +type MutationGitContextInput = subgraph_check_mutation::GitContextInput; +impl From for MutationGitContextInput { + fn from(git_context: GitContext) -> MutationGitContextInput { + MutationGitContextInput { + branch: git_context.branch, + commit: git_context.commit, + committer: git_context.author, + remote_url: git_context.remote_url, + message: None, + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct SubgraphCheckInput { + pub graph_id: String, + pub variant: String, + pub subgraph: String, + pub proposed_schema: String, + pub git_context: GitContext, + pub config: CheckConfig, +} + +impl From for MutationVariables { + fn from(input: SubgraphCheckInput) -> Self { + Self { + graph_id: input.graph_id, + variant: input.variant, + subgraph: input.subgraph, + proposed_schema: MutationSchema { + sdl: Some(input.proposed_schema), + hash: None, + }, + config: MutationConfig { + query_count_threshold: input.config.query_count_threshold, + query_count_threshold_percentage: input.config.query_count_threshold_percentage, + from: input.config.validation_period_from, + to: input.config.validation_period_to, + // we don't support configuring these, but we can't leave them out + excluded_clients: None, + ignored_operations: None, + included_variants: None, + }, + git_context: input.git_context.into(), + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct CompositionError { + pub message: String, + pub code: Option, +} diff --git a/crates/rover-client/src/query/subgraph/delete/delete_mutation.graphql b/crates/rover-client/src/operations/subgraph/delete/delete_mutation.graphql similarity index 100% rename from crates/rover-client/src/query/subgraph/delete/delete_mutation.graphql rename to crates/rover-client/src/operations/subgraph/delete/delete_mutation.graphql diff --git a/crates/rover-client/src/query/subgraph/delete/mod.rs b/crates/rover-client/src/operations/subgraph/delete/mod.rs similarity index 62% rename from crates/rover-client/src/query/subgraph/delete/mod.rs rename to crates/rover-client/src/operations/subgraph/delete/mod.rs index 788f96dd6..387f7cbc0 100644 --- a/crates/rover-client/src/query/subgraph/delete/mod.rs +++ b/crates/rover-client/src/operations/subgraph/delete/mod.rs @@ -1,6 +1,6 @@ -mod mutation_runner; +mod runner; pub(crate) mod types; -pub use mutation_runner::run; +pub use runner::run; pub use types::{SubgraphDeleteInput, SubgraphDeleteResponse}; diff --git a/crates/rover-client/src/query/subgraph/delete/mutation_runner.rs b/crates/rover-client/src/operations/subgraph/delete/runner.rs similarity index 97% rename from crates/rover-client/src/query/subgraph/delete/mutation_runner.rs rename to crates/rover-client/src/operations/subgraph/delete/runner.rs index f9ddddde0..694a7afc3 100644 --- a/crates/rover-client/src/query/subgraph/delete/mutation_runner.rs +++ b/crates/rover-client/src/operations/subgraph/delete/runner.rs @@ -1,5 +1,5 @@ use crate::blocking::StudioClient; -use crate::query::subgraph::delete::types::*; +use crate::operations::subgraph::delete::types::*; use crate::RoverClientError; use graphql_client::*; @@ -8,7 +8,7 @@ use graphql_client::*; // The paths are relative to the directory where your `Cargo.toml` is located. // Both json and the GraphQL schema language are supported as sources for the schema #[graphql( - query_path = "src/query/subgraph/delete/delete_mutation.graphql", + query_path = "src/operations/subgraph/delete/delete_mutation.graphql", schema_path = ".schema/schema.graphql", response_derives = "PartialEq, Debug, Serialize, Deserialize", deprecated = "warn" diff --git a/crates/rover-client/src/query/subgraph/delete/types.rs b/crates/rover-client/src/operations/subgraph/delete/types.rs similarity index 94% rename from crates/rover-client/src/query/subgraph/delete/types.rs rename to crates/rover-client/src/operations/subgraph/delete/types.rs index 82a82358f..04ce6c2e6 100644 --- a/crates/rover-client/src/query/subgraph/delete/types.rs +++ b/crates/rover-client/src/operations/subgraph/delete/types.rs @@ -1,4 +1,4 @@ -use crate::query::subgraph::delete::mutation_runner::subgraph_delete_mutation; +use crate::operations::subgraph::delete::runner::subgraph_delete_mutation; pub(crate) type MutationComposition = subgraph_delete_mutation::SubgraphDeleteMutationServiceRemoveImplementingServiceAndTriggerComposition; pub(crate) type MutationVariables = subgraph_delete_mutation::Variables; diff --git a/crates/rover-client/src/query/subgraph/fetch/fetch_query.graphql b/crates/rover-client/src/operations/subgraph/fetch/fetch_query.graphql similarity index 100% rename from crates/rover-client/src/query/subgraph/fetch/fetch_query.graphql rename to crates/rover-client/src/operations/subgraph/fetch/fetch_query.graphql diff --git a/crates/rover-client/src/query/subgraph/fetch/mod.rs b/crates/rover-client/src/operations/subgraph/fetch/mod.rs similarity index 78% rename from crates/rover-client/src/query/subgraph/fetch/mod.rs rename to crates/rover-client/src/operations/subgraph/fetch/mod.rs index 7c1ea61cc..57ec30cd3 100644 --- a/crates/rover-client/src/query/subgraph/fetch/mod.rs +++ b/crates/rover-client/src/operations/subgraph/fetch/mod.rs @@ -1,3 +1,3 @@ -pub mod query_runner; +pub mod runner; pub(crate) mod types; pub use types::{SubgraphFetchInput, SubgraphFetchResponse}; diff --git a/crates/rover-client/src/query/subgraph/fetch/query_runner.rs b/crates/rover-client/src/operations/subgraph/fetch/runner.rs similarity index 98% rename from crates/rover-client/src/query/subgraph/fetch/query_runner.rs rename to crates/rover-client/src/operations/subgraph/fetch/runner.rs index b365f45f4..e23032c57 100644 --- a/crates/rover-client/src/query/subgraph/fetch/query_runner.rs +++ b/crates/rover-client/src/operations/subgraph/fetch/runner.rs @@ -7,7 +7,7 @@ use graphql_client::*; // The paths are relative to the directory where your `Cargo.toml` is located. // Both json and the GraphQL schema language are supported as sources for the schema #[graphql( - query_path = "src/query/subgraph/fetch/fetch_query.graphql", + query_path = "src/operations/subgraph/fetch/fetch_query.graphql", schema_path = ".schema/schema.graphql", response_derives = "PartialEq, Debug, Serialize, Deserialize", deprecated = "warn" diff --git a/crates/rover-client/src/query/subgraph/fetch/types.rs b/crates/rover-client/src/operations/subgraph/fetch/types.rs similarity index 96% rename from crates/rover-client/src/query/subgraph/fetch/types.rs rename to crates/rover-client/src/operations/subgraph/fetch/types.rs index d3a118892..e0554de85 100644 --- a/crates/rover-client/src/query/subgraph/fetch/types.rs +++ b/crates/rover-client/src/operations/subgraph/fetch/types.rs @@ -1,4 +1,4 @@ -use super::query_runner::subgraph_fetch_query; +use super::runner::subgraph_fetch_query; pub(crate) type ServiceList = Vec; pub(crate) type SubgraphFetchResponseData = subgraph_fetch_query::ResponseData; diff --git a/crates/rover-client/src/query/subgraph/introspect/introspect_query.graphql b/crates/rover-client/src/operations/subgraph/introspect/introspect_query.graphql similarity index 100% rename from crates/rover-client/src/query/subgraph/introspect/introspect_query.graphql rename to crates/rover-client/src/operations/subgraph/introspect/introspect_query.graphql diff --git a/crates/rover-client/src/query/subgraph/introspect/introspect_schema.graphql b/crates/rover-client/src/operations/subgraph/introspect/introspect_schema.graphql similarity index 100% rename from crates/rover-client/src/query/subgraph/introspect/introspect_schema.graphql rename to crates/rover-client/src/operations/subgraph/introspect/introspect_schema.graphql diff --git a/crates/rover-client/src/query/subgraph/introspect/mod.rs b/crates/rover-client/src/operations/subgraph/introspect/mod.rs similarity index 62% rename from crates/rover-client/src/query/subgraph/introspect/mod.rs rename to crates/rover-client/src/operations/subgraph/introspect/mod.rs index e16e29777..047cf54f2 100644 --- a/crates/rover-client/src/query/subgraph/introspect/mod.rs +++ b/crates/rover-client/src/operations/subgraph/introspect/mod.rs @@ -1,5 +1,5 @@ -pub(crate) mod query_runner; +pub(crate) mod runner; pub(crate) mod types; -pub use query_runner::run; +pub use runner::run; pub use types::{SubgraphIntrospectInput, SubgraphIntrospectResponse}; diff --git a/crates/rover-client/src/query/subgraph/introspect/query_runner.rs b/crates/rover-client/src/operations/subgraph/introspect/runner.rs similarity index 85% rename from crates/rover-client/src/query/subgraph/introspect/query_runner.rs rename to crates/rover-client/src/operations/subgraph/introspect/runner.rs index a46ce5084..1a6fe7f13 100644 --- a/crates/rover-client/src/query/subgraph/introspect/query_runner.rs +++ b/crates/rover-client/src/operations/subgraph/introspect/runner.rs @@ -1,13 +1,13 @@ use crate::blocking::GraphQLClient; -use crate::query::subgraph::introspect::types::*; +use crate::operations::subgraph::introspect::types::*; use crate::RoverClientError; use graphql_client::*; #[derive(GraphQLQuery)] #[graphql( - query_path = "src/query/subgraph/introspect/introspect_query.graphql", - schema_path = "src/query/subgraph/introspect/introspect_schema.graphql", + query_path = "src/operations/subgraph/introspect/introspect_query.graphql", + schema_path = "src/operations//subgraph/introspect/introspect_schema.graphql", response_derives = "PartialEq, Debug, Serialize, Deserialize", deprecated = "warn" )] diff --git a/crates/rover-client/src/query/subgraph/introspect/types.rs b/crates/rover-client/src/operations/subgraph/introspect/types.rs similarity index 86% rename from crates/rover-client/src/query/subgraph/introspect/types.rs rename to crates/rover-client/src/operations/subgraph/introspect/types.rs index 6f3301964..0b1a933c7 100644 --- a/crates/rover-client/src/query/subgraph/introspect/types.rs +++ b/crates/rover-client/src/operations/subgraph/introspect/types.rs @@ -1,4 +1,4 @@ -use crate::query::subgraph::introspect::query_runner::subgraph_introspect_query; +use crate::operations::subgraph::introspect::runner::subgraph_introspect_query; pub(crate) type QueryVariables = subgraph_introspect_query::Variables; pub(crate) type QueryResponseData = subgraph_introspect_query::ResponseData; diff --git a/crates/rover-client/src/query/subgraph/list/list_query.graphql b/crates/rover-client/src/operations/subgraph/list/list_query.graphql similarity index 100% rename from crates/rover-client/src/query/subgraph/list/list_query.graphql rename to crates/rover-client/src/operations/subgraph/list/list_query.graphql diff --git a/crates/rover-client/src/query/subgraph/list/mod.rs b/crates/rover-client/src/operations/subgraph/list/mod.rs similarity index 64% rename from crates/rover-client/src/query/subgraph/list/mod.rs rename to crates/rover-client/src/operations/subgraph/list/mod.rs index 07495e228..aeb6b0a14 100644 --- a/crates/rover-client/src/query/subgraph/list/mod.rs +++ b/crates/rover-client/src/operations/subgraph/list/mod.rs @@ -1,6 +1,6 @@ -mod query_runner; +mod runner; pub(crate) mod types; -pub use query_runner::run; +pub use runner::run; pub use types::{SubgraphListInput, SubgraphListResponse}; diff --git a/crates/rover-client/src/query/subgraph/list/query_runner.rs b/crates/rover-client/src/operations/subgraph/list/runner.rs similarity index 98% rename from crates/rover-client/src/query/subgraph/list/query_runner.rs rename to crates/rover-client/src/operations/subgraph/list/runner.rs index 368744417..0b701d6be 100644 --- a/crates/rover-client/src/query/subgraph/list/query_runner.rs +++ b/crates/rover-client/src/operations/subgraph/list/runner.rs @@ -1,5 +1,5 @@ use crate::blocking::StudioClient; -use crate::query::subgraph::list::types::*; +use crate::operations::subgraph::list::types::*; use crate::RoverClientError; use graphql_client::*; @@ -9,7 +9,7 @@ type Timestamp = String; // The paths are relative to the directory where your `Cargo.toml` is located. // Both json and the GraphQL schema language are supported as sources for the schema #[graphql( - query_path = "src/query/subgraph/list/list_query.graphql", + query_path = "src/operations/subgraph/list/list_query.graphql", schema_path = ".schema/schema.graphql", response_derives = "PartialEq, Debug, Serialize, Deserialize", deprecated = "warn" diff --git a/crates/rover-client/src/query/subgraph/list/types.rs b/crates/rover-client/src/operations/subgraph/list/types.rs similarity index 94% rename from crates/rover-client/src/query/subgraph/list/types.rs rename to crates/rover-client/src/operations/subgraph/list/types.rs index ef69c1cb7..07235cfd0 100644 --- a/crates/rover-client/src/query/subgraph/list/types.rs +++ b/crates/rover-client/src/operations/subgraph/list/types.rs @@ -1,4 +1,4 @@ -use crate::query::subgraph::list::query_runner::subgraph_list_query; +use crate::operations::subgraph::list::runner::subgraph_list_query; pub(crate) type QuerySubgraphInfo = subgraph_list_query::SubgraphListQueryServiceImplementingServicesOnFederatedImplementingServicesServices; pub(crate) type QueryResponseData = subgraph_list_query::ResponseData; diff --git a/crates/rover-client/src/query/subgraph/mod.rs b/crates/rover-client/src/operations/subgraph/mod.rs similarity index 100% rename from crates/rover-client/src/query/subgraph/mod.rs rename to crates/rover-client/src/operations/subgraph/mod.rs diff --git a/crates/rover-client/src/query/subgraph/publish/mod.rs b/crates/rover-client/src/operations/subgraph/publish/mod.rs similarity index 77% rename from crates/rover-client/src/query/subgraph/publish/mod.rs rename to crates/rover-client/src/operations/subgraph/publish/mod.rs index b0504e084..8ad8f0747 100644 --- a/crates/rover-client/src/query/subgraph/publish/mod.rs +++ b/crates/rover-client/src/operations/subgraph/publish/mod.rs @@ -1,3 +1,3 @@ -pub mod mutation_runner; +pub mod runner; pub(crate) mod types; pub use types::{SubgraphPublishInput, SubgraphPublishResponse}; diff --git a/crates/rover-client/src/query/subgraph/publish/publish_mutation.graphql b/crates/rover-client/src/operations/subgraph/publish/publish_mutation.graphql similarity index 100% rename from crates/rover-client/src/query/subgraph/publish/publish_mutation.graphql rename to crates/rover-client/src/operations/subgraph/publish/publish_mutation.graphql diff --git a/crates/rover-client/src/query/subgraph/publish/mutation_runner.rs b/crates/rover-client/src/operations/subgraph/publish/runner.rs similarity index 97% rename from crates/rover-client/src/query/subgraph/publish/mutation_runner.rs rename to crates/rover-client/src/operations/subgraph/publish/runner.rs index 0b3ffe42c..b85154b5e 100644 --- a/crates/rover-client/src/query/subgraph/publish/mutation_runner.rs +++ b/crates/rover-client/src/operations/subgraph/publish/runner.rs @@ -1,6 +1,6 @@ use super::types::*; use crate::blocking::StudioClient; -use crate::query::config::is_federated; +use crate::operations::config::is_federated; use crate::RoverClientError; use graphql_client::*; @@ -8,7 +8,7 @@ use graphql_client::*; // The paths are relative to the directory where your `Cargo.toml` is located. // Both json and the GraphQL schema language are supported as sources for the schema #[graphql( - query_path = "src/query/subgraph/publish/publish_mutation.graphql", + query_path = "src/operations/subgraph/publish/publish_mutation.graphql", schema_path = ".schema/schema.graphql", response_derives = "PartialEq, Debug, Serialize, Deserialize", deprecated = "warn" diff --git a/crates/rover-client/src/query/subgraph/publish/types.rs b/crates/rover-client/src/operations/subgraph/publish/types.rs similarity index 95% rename from crates/rover-client/src/query/subgraph/publish/types.rs rename to crates/rover-client/src/operations/subgraph/publish/types.rs index 26738cf78..e51b32d67 100644 --- a/crates/rover-client/src/query/subgraph/publish/types.rs +++ b/crates/rover-client/src/operations/subgraph/publish/types.rs @@ -1,6 +1,6 @@ -use super::mutation_runner::subgraph_publish_mutation; +use super::runner::subgraph_publish_mutation; -use crate::utils::GitContext; +use crate::shared::GitContext; pub(crate) type ResponseData = subgraph_publish_mutation::ResponseData; pub(crate) type MutationVariables = subgraph_publish_mutation::Variables; diff --git a/crates/rover-client/src/query/supergraph/fetch.graphql b/crates/rover-client/src/operations/supergraph/fetch.graphql similarity index 100% rename from crates/rover-client/src/query/supergraph/fetch.graphql rename to crates/rover-client/src/operations/supergraph/fetch.graphql diff --git a/crates/rover-client/src/query/supergraph/fetch.rs b/crates/rover-client/src/operations/supergraph/fetch.rs similarity index 99% rename from crates/rover-client/src/query/supergraph/fetch.rs rename to crates/rover-client/src/operations/supergraph/fetch.rs index dc1b7b235..5e7a05b5d 100644 --- a/crates/rover-client/src/query/supergraph/fetch.rs +++ b/crates/rover-client/src/operations/supergraph/fetch.rs @@ -10,7 +10,7 @@ type GraphQLDocument = String; // The paths are relative to the directory where your `Cargo.toml` is located. // Both json and the GraphQL schema language are supported as sources for the schema #[graphql( - query_path = "src/query/supergraph/fetch.graphql", + query_path = "src/operations/supergraph/fetch.graphql", schema_path = ".schema/schema.graphql", response_derives = "PartialEq, Debug, Serialize, Deserialize", deprecated = "warn" diff --git a/crates/rover-client/src/query/supergraph/mod.rs b/crates/rover-client/src/operations/supergraph/mod.rs similarity index 100% rename from crates/rover-client/src/query/supergraph/mod.rs rename to crates/rover-client/src/operations/supergraph/mod.rs diff --git a/crates/rover-client/src/query/subgraph/check/mod.rs b/crates/rover-client/src/query/subgraph/check/mod.rs deleted file mode 100644 index 4fd7b2ce8..000000000 --- a/crates/rover-client/src/query/subgraph/check/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod query_runner; -pub(crate) mod types; -pub use types::{SubgraphCheckConfig, SubgraphCheckInput, SubgraphCheckResponse}; diff --git a/crates/rover-client/src/query/subgraph/check/types.rs b/crates/rover-client/src/query/subgraph/check/types.rs deleted file mode 100644 index f339a6b1e..000000000 --- a/crates/rover-client/src/query/subgraph/check/types.rs +++ /dev/null @@ -1,114 +0,0 @@ -use std::fmt; - -use crate::utils::GitContext; - -use super::query_runner::subgraph_check_query; - -pub(crate) type Timestamp = String; -type QueryVariables = subgraph_check_query::Variables; -type QueryChangeSeverity = subgraph_check_query::ChangeSeverity; -type QuerySchema = subgraph_check_query::PartialSchemaInput; -type QueryConfig = subgraph_check_query::HistoricQueryParameters; -type GitContextInput = subgraph_check_query::GitContextInput; - -#[derive(Debug, Clone, PartialEq)] -pub struct SubgraphCheckInput { - pub graph_id: String, - pub variant: String, - pub subgraph: String, - pub proposed_schema: String, - pub git_context: GitContext, - pub config: SubgraphCheckConfig, -} - -#[derive(Debug, Clone, PartialEq)] -pub struct SubgraphCheckConfig { - pub query_count_threshold: Option, - pub query_count_threshold_percentage: Option, - pub validation_period_from: Option, - pub validation_period_to: Option, -} - -impl From for QueryVariables { - fn from(input: SubgraphCheckInput) -> Self { - Self { - graph_id: input.graph_id, - variant: input.variant, - subgraph: input.subgraph, - proposed_schema: QuerySchema { - sdl: Some(input.proposed_schema), - hash: None, - }, - config: QueryConfig { - query_count_threshold: input.config.query_count_threshold, - query_count_threshold_percentage: input.config.query_count_threshold_percentage, - from: input.config.validation_period_from, - to: input.config.validation_period_to, - // we don't support configuring these, but we can't leave them out - excluded_clients: None, - ignored_operations: None, - included_variants: None, - }, - git_context: input.git_context.into(), - } - } -} - -#[derive(Debug, Clone, PartialEq)] -pub struct SubgraphCheckResponse { - pub target_url: Option, - pub number_of_checked_operations: i64, - pub changes: Vec, - pub change_severity: ChangeSeverity, -} - -#[derive(Debug, Clone, PartialEq)] -pub enum ChangeSeverity { - PASS, - FAIL, -} - -impl fmt::Display for ChangeSeverity { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let msg = match self { - ChangeSeverity::PASS => "PASS", - ChangeSeverity::FAIL => "FAIL", - }; - write!(f, "{}", msg) - } -} - -impl From for ChangeSeverity { - fn from(severity: QueryChangeSeverity) -> Self { - match severity { - QueryChangeSeverity::NOTICE => ChangeSeverity::PASS, - QueryChangeSeverity::FAILURE => ChangeSeverity::FAIL, - _ => unreachable!("Unknown change severity"), - } - } -} - -#[derive(Debug, Clone, PartialEq)] -pub struct SchemaChange { - pub code: String, - pub description: String, - pub severity: ChangeSeverity, -} - -#[derive(Debug, Clone, PartialEq)] -pub struct CompositionError { - pub message: String, - pub code: Option, -} - -impl From for GitContextInput { - fn from(git_context: GitContext) -> GitContextInput { - GitContextInput { - branch: git_context.branch, - commit: git_context.commit, - committer: git_context.author, - remote_url: git_context.remote_url, - message: None, - } - } -} diff --git a/crates/rover-client/src/shared/check_response.rs b/crates/rover-client/src/shared/check_response.rs new file mode 100644 index 000000000..48dba4159 --- /dev/null +++ b/crates/rover-client/src/shared/check_response.rs @@ -0,0 +1,104 @@ +use std::cmp::Ordering; +use std::fmt; + +use crate::RoverClientError; + +/// CheckResponse is the return type of the +/// `graph` and `subgraph` check operations +#[derive(Debug, Clone, PartialEq)] +pub struct CheckResponse { + pub target_url: Option, + pub number_of_checked_operations: i64, + pub changes: Vec, + pub change_severity: ChangeSeverity, + pub num_failures: i64, +} + +impl CheckResponse { + pub fn new( + target_url: Option, + number_of_checked_operations: i64, + changes: Vec, + change_severity: ChangeSeverity, + ) -> CheckResponse { + let mut num_failures = 0; + for change in &changes { + if let ChangeSeverity::FAIL = change.severity { + num_failures += 1; + } + } + + CheckResponse { + target_url, + number_of_checked_operations, + changes, + change_severity, + num_failures, + } + } + + pub fn check_for_failures(&self) -> Result { + match &self.num_failures.cmp(&0) { + Ordering::Equal => Ok(self.clone()), + Ordering::Greater => Err(RoverClientError::OperationCheckFailure { + check_response: self.clone(), + }), + Ordering::Less => unreachable!("Somehow encountered a negative number of failures."), + } + } +} + +/// ChangeSeverity indicates whether a proposed change +/// in a GraphQL schema passed or failed the check +#[derive(Debug, Clone, PartialEq)] +pub enum ChangeSeverity { + /// The proposed schema has passed the checks + PASS, + + /// The proposed schema has failed the checks + FAIL, +} + +impl ChangeSeverity { + // This message should be used when matching on a + // ChangeSeverity originating from auto-generated + // types from graphql-client + // + // We want to panic in this situation so that we + // get bug reports if Rover doesn't know the proper type + pub(crate) fn unreachable() -> ! { + unreachable!("Unknown change severity") + } +} + +impl fmt::Display for ChangeSeverity { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let msg = match self { + ChangeSeverity::PASS => "PASS", + ChangeSeverity::FAIL => "FAIL", + }; + write!(f, "{}", msg) + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct SchemaChange { + /// The code associated with a given change + /// e.g. 'TYPE_REMOVED' + pub code: String, + + /// Explanation of a given change + pub description: String, + + /// The severity of a given change + pub severity: ChangeSeverity, +} + +/// CheckConfig is used as an input to check operations +#[derive(Debug, Clone, PartialEq)] +pub struct CheckConfig { + pub query_count_threshold: Option, + pub query_count_threshold_percentage: Option, + pub validation_period_from: Option, + pub validation_period_to: Option, +} diff --git a/crates/rover-client/src/utils/git.rs b/crates/rover-client/src/shared/git_context.rs similarity index 95% rename from crates/rover-client/src/utils/git.rs rename to crates/rover-client/src/shared/git_context.rs index 22f2fc4d2..37fad6ba3 100644 --- a/crates/rover-client/src/utils/git.rs +++ b/crates/rover-client/src/shared/git_context.rs @@ -1,4 +1,4 @@ -use crate::query::graph; +use crate::operations::graph; use std::env; @@ -150,19 +150,6 @@ impl From for GraphPublishContextInput { } } -type GraphCheckContextInput = graph::check::check_schema_query::GitContextInput; -impl From for GraphCheckContextInput { - fn from(git_context: GitContext) -> GraphCheckContextInput { - GraphCheckContextInput { - branch: git_context.branch, - commit: git_context.commit, - committer: git_context.author, - remote_url: git_context.remote_url, - message: None, - } - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/crates/rover-client/src/shared/mod.rs b/crates/rover-client/src/shared/mod.rs new file mode 100644 index 000000000..e4d6a6267 --- /dev/null +++ b/crates/rover-client/src/shared/mod.rs @@ -0,0 +1,5 @@ +mod check_response; +mod git_context; + +pub use check_response::{ChangeSeverity, CheckConfig, CheckResponse, SchemaChange}; +pub use git_context::GitContext; diff --git a/crates/rover-client/src/utils/mod.rs b/crates/rover-client/src/utils/mod.rs deleted file mode 100644 index 2c5601aed..000000000 --- a/crates/rover-client/src/utils/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod git; - -pub use git::GitContext; diff --git a/src/cli.rs b/src/cli.rs index 1820dbce3..c67f73b78 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -11,7 +11,7 @@ use crate::utils::{ use crate::Result; use config::Config; use houston as config; -use rover_client::utils::GitContext; +use rover_client::shared::GitContext; use timber::{Level, LEVELS}; use camino::Utf8PathBuf; diff --git a/src/command/config/whoami.rs b/src/command/config/whoami.rs index 1c77dea5a..b79104660 100644 --- a/src/command/config/whoami.rs +++ b/src/command/config/whoami.rs @@ -1,5 +1,5 @@ use ansi_term::Colour::Green; -use rover_client::query::config::who_am_i::{query_runner, Actor, ConfigWhoAmIInput}; +use rover_client::operations::config::who_am_i::{runner, Actor, ConfigWhoAmIInput}; use serde::Serialize; use structopt::StructOpt; @@ -26,7 +26,7 @@ impl WhoAmI { let client = client_config.get_client(&self.profile_name)?; eprintln!("Checking identity of your API key against the registry."); - let identity = query_runner::run(ConfigWhoAmIInput {}, &client)?; + let identity = runner::run(ConfigWhoAmIInput {}, &client)?; let mut message = format!( "{}: {:?}\n", diff --git a/src/command/graph/check.rs b/src/command/graph/check.rs index 7964e349c..ac2ca8e4d 100644 --- a/src/command/graph/check.rs +++ b/src/command/graph/check.rs @@ -1,8 +1,8 @@ use serde::Serialize; use structopt::StructOpt; -use rover_client::query::graph::check; -use rover_client::utils::GitContext; +use rover_client::operations::graph::check::{self, GraphCheckInput}; +use rover_client::shared::{CheckConfig, GitContext}; use crate::command::RoverStdout; use crate::utils::client::StudioClientConfig; @@ -11,7 +11,6 @@ use crate::utils::parsers::{ parse_graph_ref, parse_query_count_threshold, parse_query_percentage_threshold, parse_schema_source, parse_validation_period, GraphRef, SchemaSource, ValidationPeriod, }; -use crate::utils::table::{self, cell, row}; use crate::Result; #[derive(Debug, Serialize, StructOpt)] @@ -56,79 +55,29 @@ impl Check { git_context: GitContext, ) -> Result { let client = client_config.get_client(&self.profile_name)?; - let sdl = load_schema_from_flag(&self.schema, std::io::stdin())?; + let proposed_schema = load_schema_from_flag(&self.schema, std::io::stdin())?; + + eprintln!( + "Checking the proposed schema against metrics from {}", + &self.graph + ); + let res = check::run( - check::check_schema_query::Variables { + GraphCheckInput { graph_id: self.graph.name.clone(), - variant: Some(self.graph.variant.clone()), - schema: Some(sdl), - git_context: git_context.into(), - config: check::check_schema_query::HistoricQueryParameters { + variant: self.graph.variant.clone(), + proposed_schema, + git_context, + config: CheckConfig { query_count_threshold: self.query_count_threshold, query_count_threshold_percentage: self.query_percentage_threshold, - from: self.validation_period.clone().unwrap_or_default().from, - to: self.validation_period.clone().unwrap_or_default().to, - // we don't support configuring these, but we can't leave them out - excluded_clients: None, - ignored_operations: None, - included_variants: None, + validation_period_from: self.validation_period.clone().unwrap_or_default().from, + validation_period_to: self.validation_period.clone().unwrap_or_default().to, }, }, &client, )?; - eprintln!( - "Validated the proposed subgraph against metrics from {}", - &self.graph - ); - - let num_changes = res.changes.len(); - - let msg = match num_changes { - 0 => "There is no difference between the proposed graph and the graph that already exists in the graph registry. Try making a change to your proposed graph before running this command.".to_string(), - _ => format!("Compared {} schema changes against {} operations", res.changes.len(), res.number_of_checked_operations), - }; - - eprintln!("{}", &msg); - - let num_failures = print_changes(&res.changes); - - if let Some(url) = res.target_url { - eprintln!("View full details at {}", &url); - } - - match num_failures { - 0 => Ok(RoverStdout::None), - 1 => Err(anyhow::anyhow!("Encountered 1 failure.").into()), - _ => Err(anyhow::anyhow!("Encountered {} failures.", num_failures).into()), - } - } -} - -fn print_changes( - checks: &[check::check_schema_query::CheckSchemaQueryServiceCheckSchemaDiffToPreviousChanges], -) -> u64 { - let mut num_failures = 0; - - if !checks.is_empty() { - let mut table = table::get_table(); - - // bc => sets top row to be bold and center - table.add_row(row![bc => "Change", "Code", "Description"]); - for check in checks { - let change = match check.severity { - check::check_schema_query::ChangeSeverity::NOTICE => "PASS", - check::check_schema_query::ChangeSeverity::FAILURE => { - num_failures += 1; - "FAIL" - } - _ => unreachable!("Unknown change severity"), - }; - table.add_row(row![change, check.code, check.description]); - } - - eprintln!("{}", table); + Ok(RoverStdout::CheckResponse(res)) } - - num_failures } diff --git a/src/command/graph/fetch.rs b/src/command/graph/fetch.rs index d3d4c1e38..e08383f0f 100644 --- a/src/command/graph/fetch.rs +++ b/src/command/graph/fetch.rs @@ -2,7 +2,7 @@ use ansi_term::Colour::{Cyan, Yellow}; use serde::Serialize; use structopt::StructOpt; -use rover_client::query::graph::fetch; +use rover_client::operations::graph::fetch; use crate::command::RoverStdout; use crate::utils::client::StudioClientConfig; diff --git a/src/command/graph/introspect.rs b/src/command/graph/introspect.rs index c76fa7512..e674702dd 100644 --- a/src/command/graph/introspect.rs +++ b/src/command/graph/introspect.rs @@ -6,7 +6,7 @@ use url::Url; use rover_client::{ blocking::GraphQLClient, - query::graph::introspect::{self, GraphIntrospectInput}, + operations::graph::introspect::{self, GraphIntrospectInput}, }; use crate::command::RoverStdout; diff --git a/src/command/graph/mod.rs b/src/command/graph/mod.rs index 4ec72fbfe..e06f7999f 100644 --- a/src/command/graph/mod.rs +++ b/src/command/graph/mod.rs @@ -10,7 +10,7 @@ use crate::command::RoverStdout; use crate::utils::client::StudioClientConfig; use crate::Result; -use rover_client::utils::GitContext; +use rover_client::shared::GitContext; #[derive(Debug, Serialize, StructOpt)] pub struct Graph { diff --git a/src/command/graph/publish.rs b/src/command/graph/publish.rs index 361608242..1526f09f1 100644 --- a/src/command/graph/publish.rs +++ b/src/command/graph/publish.rs @@ -2,8 +2,8 @@ use ansi_term::Colour::{Cyan, Yellow}; use serde::Serialize; use structopt::StructOpt; -use rover_client::query::graph::publish; -use rover_client::utils::GitContext; +use rover_client::operations::graph::publish; +use rover_client::shared::GitContext; use crate::command::RoverStdout; use crate::utils::client::StudioClientConfig; diff --git a/src/command/mod.rs b/src/command/mod.rs index dfc4d344b..7d0b6c04b 100644 --- a/src/command/mod.rs +++ b/src/command/mod.rs @@ -4,11 +4,12 @@ mod explain; mod graph; mod info; mod install; -mod output; mod subgraph; mod supergraph; mod update; +pub(crate) mod output; + pub use config::Config; pub use docs::Docs; pub use explain::Explain; diff --git a/src/command/output.rs b/src/command/output.rs index c2870532f..685876718 100644 --- a/src/command/output.rs +++ b/src/command/output.rs @@ -6,7 +6,8 @@ use crate::utils::table::{self, cell, row}; use ansi_term::{Colour::Yellow, Style}; use atty::Stream; use crossterm::style::Attribute::Underlined; -use rover_client::query::subgraph::{check::SubgraphCheckResponse, list::SubgraphListResponse}; +use rover_client::operations::subgraph::list::SubgraphListResponse; +use rover_client::shared::CheckResponse; use termimad::MadSkin; /// RoverStdout defines all of the different types of data that are printed @@ -25,7 +26,7 @@ pub enum RoverStdout { CoreSchema(String), SchemaHash(String), SubgraphList(SubgraphListResponse), - SubgraphCheck(SubgraphCheckResponse), + CheckResponse(CheckResponse), VariantList(Vec), Profiles(Vec), Introspection(String), @@ -99,35 +100,8 @@ impl RoverStdout { details.root_url, details.graph_name ); } - RoverStdout::SubgraphCheck(check_response) => { - let num_changes = check_response.changes.len(); - - let msg = match num_changes { - 0 => "There were no changes detected in the composed schema.".to_string(), - _ => format!( - "Compared {} schema changes against {} operations", - check_response.changes.len(), - check_response.number_of_checked_operations - ), - }; - - eprintln!("{}", &msg); - - if !check_response.changes.is_empty() { - let mut table = table::get_table(); - - // bc => sets top row to be bold and center - table.add_row(row![bc => "Change", "Code", "Description"]); - for check in &check_response.changes { - table.add_row(row![check.severity, check.code, check.description]); - } - - print_content(table.to_string()); - } - - if let Some(url) = &check_response.target_url { - eprintln!("View full details at {}", url); - } + RoverStdout::CheckResponse(check_response) => { + print_check_response(check_response); } RoverStdout::VariantList(variants) => { print_descriptor("Variants"); @@ -186,3 +160,34 @@ fn print_content(content: impl Display) { print!("{}", content) } } + +pub(crate) fn print_check_response(check_response: &CheckResponse) { + let num_changes = check_response.changes.len(); + + let msg = match num_changes { + 0 => "There were no changes detected in the composed schema.".to_string(), + _ => format!( + "Compared {} schema changes against {} operations", + check_response.changes.len(), + check_response.number_of_checked_operations + ), + }; + + eprintln!("{}", &msg); + + if !check_response.changes.is_empty() { + let mut table = table::get_table(); + + // bc => sets top row to be bold and center + table.add_row(row![bc => "Change", "Code", "Description"]); + for check in &check_response.changes { + table.add_row(row![check.severity, check.code, check.description]); + } + + print_content(&table); + } + + if let Some(url) = &check_response.target_url { + eprintln!("View full details at {}", url); + } +} diff --git a/src/command/subgraph/check.rs b/src/command/subgraph/check.rs index f6c3bc3b1..de6780d87 100644 --- a/src/command/subgraph/check.rs +++ b/src/command/subgraph/check.rs @@ -1,8 +1,8 @@ use serde::Serialize; use structopt::StructOpt; -use rover_client::query::subgraph::check::{query_runner, SubgraphCheckConfig, SubgraphCheckInput}; -use rover_client::utils::GitContext; +use rover_client::operations::subgraph::check::{runner, SubgraphCheckInput}; +use rover_client::shared::{CheckConfig, GitContext}; use crate::command::RoverStdout; use crate::utils::client::StudioClientConfig; @@ -68,14 +68,14 @@ impl Check { &self.subgraph, &self.graph ); - let res = query_runner::run( + let res = runner::run( SubgraphCheckInput { graph_id: self.graph.name.clone(), variant: self.graph.variant.clone(), proposed_schema, subgraph: self.subgraph.clone(), git_context, - config: SubgraphCheckConfig { + config: CheckConfig { query_count_threshold: self.query_count_threshold, query_count_threshold_percentage: self.query_percentage_threshold, validation_period_from: self.validation_period.clone().unwrap_or_default().from, @@ -85,6 +85,6 @@ impl Check { &client, )?; - Ok(RoverStdout::SubgraphCheck(res)) + Ok(RoverStdout::CheckResponse(res)) } } diff --git a/src/command/subgraph/delete.rs b/src/command/subgraph/delete.rs index bcf7c61d4..09bcf5dd3 100644 --- a/src/command/subgraph/delete.rs +++ b/src/command/subgraph/delete.rs @@ -7,7 +7,9 @@ use crate::utils::client::StudioClientConfig; use crate::utils::parsers::{parse_graph_ref, GraphRef}; use crate::Result; -use rover_client::query::subgraph::delete::{self, SubgraphDeleteInput, SubgraphDeleteResponse}; +use rover_client::operations::subgraph::delete::{ + self, SubgraphDeleteInput, SubgraphDeleteResponse, +}; #[derive(Debug, Serialize, StructOpt)] pub struct Delete { diff --git a/src/command/subgraph/fetch.rs b/src/command/subgraph/fetch.rs index 7319fadd2..08ddada32 100644 --- a/src/command/subgraph/fetch.rs +++ b/src/command/subgraph/fetch.rs @@ -2,7 +2,7 @@ use ansi_term::Colour::{Cyan, Yellow}; use serde::Serialize; use structopt::StructOpt; -use rover_client::query::subgraph::fetch::{query_runner, SubgraphFetchInput}; +use rover_client::operations::subgraph::fetch::{runner, SubgraphFetchInput}; use crate::command::RoverStdout; use crate::utils::client::StudioClientConfig; @@ -39,7 +39,7 @@ impl Fetch { Yellow.normal().paint(&self.profile_name) ); - let result = query_runner::run( + let result = runner::run( SubgraphFetchInput { graph_id: self.graph.name.clone(), variant: self.graph.variant.clone(), diff --git a/src/command/subgraph/introspect.rs b/src/command/subgraph/introspect.rs index 884cfe5c8..d514f5639 100644 --- a/src/command/subgraph/introspect.rs +++ b/src/command/subgraph/introspect.rs @@ -5,7 +5,7 @@ use url::Url; use rover_client::{ blocking::GraphQLClient, - query::subgraph::introspect::{self, SubgraphIntrospectInput}, + operations::subgraph::introspect::{self, SubgraphIntrospectInput}, }; use crate::command::RoverStdout; diff --git a/src/command/subgraph/list.rs b/src/command/subgraph/list.rs index 28c122a48..d24ece7e2 100644 --- a/src/command/subgraph/list.rs +++ b/src/command/subgraph/list.rs @@ -2,7 +2,7 @@ use ansi_term::Colour::Cyan; use serde::Serialize; use structopt::StructOpt; -use rover_client::query::subgraph::list::{self, SubgraphListInput}; +use rover_client::operations::subgraph::list::{self, SubgraphListInput}; use crate::command::RoverStdout; use crate::utils::client::StudioClientConfig; diff --git a/src/command/subgraph/mod.rs b/src/command/subgraph/mod.rs index 6dbf262e3..d7a45ae94 100644 --- a/src/command/subgraph/mod.rs +++ b/src/command/subgraph/mod.rs @@ -12,7 +12,7 @@ use crate::command::RoverStdout; use crate::utils::client::StudioClientConfig; use crate::Result; -use rover_client::utils::GitContext; +use rover_client::shared::GitContext; #[derive(Debug, Serialize, StructOpt)] pub struct Subgraph { diff --git a/src/command/subgraph/publish.rs b/src/command/subgraph/publish.rs index 5314d4646..d84c6a0ae 100644 --- a/src/command/subgraph/publish.rs +++ b/src/command/subgraph/publish.rs @@ -10,8 +10,10 @@ use crate::utils::{ }; use crate::Result; -use rover_client::query::subgraph::publish::{self, SubgraphPublishInput, SubgraphPublishResponse}; -use rover_client::utils::GitContext; +use rover_client::operations::subgraph::publish::{ + self, SubgraphPublishInput, SubgraphPublishResponse, +}; +use rover_client::shared::GitContext; #[derive(Debug, Serialize, StructOpt)] pub struct Publish { @@ -68,7 +70,7 @@ impl Publish { tracing::debug!("Publishing \n{}", &schema); - let publish_response = publish::mutation_runner::run( + let publish_response = publish::runner::run( SubgraphPublishInput { graph_id: self.graph.name.clone(), variant: self.graph.variant.clone(), diff --git a/src/command/supergraph/compose/do_compose.rs b/src/command/supergraph/compose/do_compose.rs index d775e627b..99ad0b9fc 100644 --- a/src/command/supergraph/compose/do_compose.rs +++ b/src/command/supergraph/compose/do_compose.rs @@ -5,11 +5,11 @@ use crate::{anyhow, command::RoverStdout, error::RoverError, Result, Suggestion} use ansi_term::Colour::Red; use camino::Utf8PathBuf; -use rover_client::query::subgraph::fetch::SubgraphFetchInput; -use rover_client::query::subgraph::introspect::SubgraphIntrospectInput; +use rover_client::operations::subgraph::fetch::SubgraphFetchInput; +use rover_client::operations::subgraph::introspect::SubgraphIntrospectInput; use rover_client::{ blocking::GraphQLClient, - query::subgraph::{fetch, introspect}, + operations::subgraph::{fetch, introspect}, }; use serde::Serialize; use std::{collections::HashMap, fs}; @@ -129,7 +129,7 @@ pub(crate) fn get_subgraph_definitions( // obtain SDL and add it to subgraph_definition. let client = client_config.get_client(&profile_name)?; let graphref = parse_graph_ref(graphref)?; - let result = fetch::query_runner::run( + let result = fetch::runner::run( SubgraphFetchInput { graph_id: graphref.name.clone(), variant: graphref.variant.clone(), diff --git a/src/command/supergraph/fetch.rs b/src/command/supergraph/fetch.rs index 8176f0297..d55cdd384 100644 --- a/src/command/supergraph/fetch.rs +++ b/src/command/supergraph/fetch.rs @@ -2,7 +2,7 @@ use crate::utils::client::StudioClientConfig; use crate::utils::parsers::{parse_graph_ref, GraphRef}; use crate::{command::RoverStdout, Result}; -use rover_client::query::supergraph::fetch; +use rover_client::operations::supergraph::fetch; use ansi_term::Colour::{Cyan, Yellow}; use serde::Serialize; diff --git a/src/error/metadata/code.rs b/src/error/metadata/code.rs index c50932b4b..86782c40b 100644 --- a/src/error/metadata/code.rs +++ b/src/error/metadata/code.rs @@ -35,6 +35,7 @@ pub enum Code { E027, E028, E029, + E030, } impl Display for Code { @@ -77,6 +78,7 @@ impl Code { (Code::E027, include_str!("./codes/E027.md").to_string()), (Code::E028, include_str!("./codes/E028.md").to_string()), (Code::E029, include_str!("./codes/E029.md").to_string()), + (Code::E030, include_str!("./codes/E030.md").to_string()), ]; contents.into_iter().collect() } @@ -86,8 +88,8 @@ impl Code { pub fn explain(&self) -> String { let all_explanations = Code::explanations(); let explanation = all_explanations.get(self); - if let Some(expl) = explanation { - format!("**{}**\n\n{}\n\n", self.to_string(), expl.clone()) + if let Some(explanation) = explanation { + format!("**{}**\n\n{}\n\n", &self, &explanation) } else { "Explanation not available".to_string() } diff --git a/src/error/metadata/codes/E030.md b/src/error/metadata/codes/E030.md new file mode 100644 index 000000000..b761379ac --- /dev/null +++ b/src/error/metadata/codes/E030.md @@ -0,0 +1 @@ +This error occurs when an operation check fails. This means that you proposed a schema that would break operations in use by existing clients. You can configure this behavior in the Checks -> Configuration view in [Apollo Studio](https://studio.apollographql.com/), and you can read more about client checks [here](https://www.apollographql.com/docs/studio/schema-checks/). \ No newline at end of file diff --git a/src/error/metadata/mod.rs b/src/error/metadata/mod.rs index 911b44b43..ab4ecd1a8 100644 --- a/src/error/metadata/mod.rs +++ b/src/error/metadata/mod.rs @@ -7,7 +7,7 @@ pub use suggestion::Suggestion; use houston::HoustonProblem; use rover_client::RoverClientError; -use crate::utils::env::RoverEnvKey; +use crate::{command::output::print_check_response, utils::env::RoverEnvKey}; use std::{env, fmt::Display}; @@ -79,6 +79,15 @@ impl From<&mut anyhow::Error> for Metadata { Some(Code::E029), ) } + RoverClientError::OperationCheckFailure { check_response } => { + print_check_response(check_response); + ( + Some(Suggestion::Adhoc( + "TODO: make a new error code and markdown file linking to client checks".to_string(), + )), + None, + ) + } RoverClientError::SubgraphIntrospectionNotAvailable => { (Some(Suggestion::UseFederatedGraph), Some(Code::E007)) } diff --git a/src/error/metadata/suggestion.rs b/src/error/metadata/suggestion.rs index 5599eefa4..a1d25f949 100644 --- a/src/error/metadata/suggestion.rs +++ b/src/error/metadata/suggestion.rs @@ -35,6 +35,9 @@ pub enum Suggestion { FixSubgraphSchema { graph_name: String, }, + FixOperationsInSchema { + graph_name: String, + }, } impl Display for Suggestion { @@ -132,7 +135,8 @@ impl Display for Suggestion { Suggestion::CheckServerConnection => "Make sure the endpoint is accepting connections and is spelled correctly".to_string(), Suggestion::ConvertGraphToSubgraph => "If you are sure you want to convert a non-federated graph to a subgraph, you can re-run the same command with a `--convert` flag.".to_string(), Suggestion::CheckGnuVersion => "This is likely an issue with your current version of `glibc`. Try running `ldd --version`, and if the version >= 2.18, we suggest installing the Rover binary built for `x86_64-unknown-linux-gnu`".to_string(), - Suggestion::FixSubgraphSchema { graph_name } => format!("The changes in the schema you proposed are incompatible with graph {}. See {} for more information on resolving composition errors.", Yellow.normal().paint(graph_name), Cyan.normal().paint("https://www.apollographql.com/docs/federation/errors/")) + Suggestion::FixSubgraphSchema { graph_name } => format!("The changes in the schema you proposed are incompatible with graph {}. See {} for more information on resolving composition errors.", Yellow.normal().paint(graph_name), Cyan.normal().paint("https://www.apollographql.com/docs/federation/errors/")), + Suggestion::FixOperationsInSchema { graph_name } => format!("The changes in the schema you proposed are incompatible with graph {}. See {} for more information on resolving operation check errors.", Yellow.normal().paint(graph_name), Cyan.normal().paint("https://www.apollographql.com/docs/studio/schema-checks/")) }; write!(formatter, "{}", &suggestion) }