-
Notifications
You must be signed in to change notification settings - Fork 85
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
9f22db6
commit 5dfe6d7
Showing
7 changed files
with
237 additions
and
47 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
mutation CheckPartialSchemaQuery ( | ||
$graph_id: ID! | ||
$variant: String! | ||
$implementingServiceName: String! | ||
$partialSchema: PartialSchemaInput! | ||
) { | ||
service(id: $graph_id) { | ||
checkPartialSchema( | ||
graphVariant: $variant | ||
implementingServiceName: $implementingServiceName | ||
partialSchema: $partialSchema | ||
) { | ||
compositionValidationResult { | ||
compositionValidationDetails { | ||
schemaHash | ||
} | ||
graphCompositionID | ||
errors { | ||
message | ||
} | ||
} | ||
checkSchemaResult { | ||
diffToPrevious { | ||
severity | ||
affectedClients { | ||
__typename | ||
} | ||
affectedQueries { | ||
__typename | ||
} | ||
numberOfCheckedOperations | ||
changes { | ||
severity | ||
code | ||
description | ||
} | ||
validationConfig { | ||
from | ||
to | ||
queryCountThreshold | ||
queryCountThresholdPercentage | ||
} | ||
} | ||
targetUrl | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
use crate::blocking::StudioClient; | ||
use crate::RoverClientError; | ||
use graphql_client::*; | ||
|
||
use reqwest::Url; | ||
|
||
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.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_partial_schema_query | ||
pub struct CheckPartialSchemaQuery; | ||
|
||
/// The main function to be used from this module. | ||
/// This function takes a proposed schema and validates it against a pushed | ||
/// schema. | ||
pub fn run( | ||
variables: check_partial_schema_query::Variables, | ||
client: &StudioClient, | ||
) -> Result<CheckResponse, RoverClientError> { | ||
let data = client.post::<CheckPartialSchemaQuery>(variables)?; | ||
get_check_response_from_data(data) | ||
} | ||
|
||
#[derive(Debug)] | ||
pub struct CheckResponse { | ||
pub target_url: Option<Url>, | ||
pub number_of_checked_operations: i64, | ||
pub change_severity: check_partial_schema_query::ChangeSeverity, | ||
pub changes: Vec<check_partial_schema_query::CheckPartialSchemaQueryServiceCheckPartialSchemaCheckSchemaResultDiffToPreviousChanges> | ||
} | ||
|
||
fn get_check_response_from_data( | ||
data: check_partial_schema_query::ResponseData, | ||
) -> Result<CheckResponse, RoverClientError> { | ||
let service = data.service.ok_or(RoverClientError::NoService)?; | ||
|
||
// TODO: fix this error | ||
let check_schema_result = service.check_partial_schema.check_schema_result.ok_or(RoverClientError::NoData)?; | ||
let target_url = get_url(check_schema_result.target_url); | ||
|
||
let diff_to_previous = check_schema_result.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; | ||
|
||
Ok(CheckResponse { | ||
target_url, | ||
number_of_checked_operations, | ||
change_severity, | ||
changes, | ||
}) | ||
} | ||
|
||
fn get_url(url: Option<String>) -> Option<Url> { | ||
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, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,36 +1,98 @@ | ||
use std::path::PathBuf; | ||
|
||
use anyhow::Result; | ||
use anyhow::{Context, Result}; | ||
use serde::Serialize; | ||
use structopt::StructOpt; | ||
use prettytable::{cell, row, Table}; | ||
|
||
use rover_client::query::subgraph::check; | ||
|
||
use crate::client::get_studio_client; | ||
use crate::command::RoverStdout; | ||
use crate::utils::parsers::{parse_graph_ref, GraphRef}; | ||
|
||
#[derive(Debug, Serialize, StructOpt)] | ||
pub struct Check { | ||
/// ID of graph in Apollo Studio to fetch from | ||
#[structopt(name = "GRAPH_NAME")] | ||
/// <NAME>@<VARIANT> of graph in Apollo Studio to validate. | ||
/// @<VARIANT> may be left off, defaulting to @current | ||
#[structopt(name = "GRAPH_REF", parse(try_from_str = parse_graph_ref))] | ||
#[serde(skip_serializing)] | ||
graph_name: String, | ||
graph: GraphRef, | ||
|
||
/// Name of graph variant in Apollo Studio to fetch from | ||
#[structopt(long, default_value = "current")] | ||
/// Name of the implementing service to validate | ||
#[structopt(long = "service", required = true)] | ||
#[serde(skip_serializing)] | ||
variant: String, | ||
service_name: String, | ||
|
||
/// Name of configuration profile to use | ||
#[structopt(long = "profile", default_value = "default")] | ||
#[serde(skip_serializing)] | ||
profile_name: String, | ||
|
||
/// Path of .graphql/.gql schema file to push | ||
#[structopt(long, short = "s")] | ||
#[structopt(long = "schema", short = "s")] | ||
#[serde(skip_serializing)] | ||
schema: PathBuf, | ||
schema_path: PathBuf, | ||
} | ||
|
||
impl Check { | ||
pub fn run(&self) -> Result<RoverStdout> { | ||
Ok(RoverStdout::None) | ||
let client = | ||
get_studio_client(&self.profile_name).context("Failed to get studio client")?; | ||
let sdl = std::fs::read_to_string(&self.schema_path).with_context(|| format!("Could not read file `{}`", &self.schema_path.display()))?; | ||
let partial_schema = check::check_partial_schema_query::PartialSchemaInput { | ||
sdl: Some(sdl), | ||
hash: None | ||
}; | ||
let res = check::run( | ||
check::check_partial_schema_query::Variables { | ||
graph_id: self.graph.name.clone(), | ||
variant: self.graph.variant.clone(), | ||
partial_schema, | ||
implementing_service_name: self.service_name.clone() | ||
}, | ||
&client, | ||
) | ||
.context("Failed to validate schema")?; | ||
|
||
tracing::info!( | ||
"Validated schema against metrics from variant {} on subgraph {}", | ||
&self.graph.variant, | ||
&self.graph.name | ||
); | ||
tracing::info!( | ||
"Compared {} schema changes against {} operations", | ||
res.changes.len(), | ||
res.number_of_checked_operations | ||
); | ||
|
||
if let Some(url) = res.target_url { | ||
tracing::info!("View full details here"); | ||
tracing::info!("{}", url.to_string()); | ||
} | ||
|
||
let mut table = Table::new(); | ||
|
||
let mut num_failures: u64 = 0; | ||
table.add_row(row!["Change", "Code", "Description"]); | ||
for check in res.changes { | ||
let change = match check.severity { | ||
check::check_partial_schema_query::ChangeSeverity::NOTICE => "PASS", | ||
check::check_partial_schema_query::ChangeSeverity::FAILURE => { | ||
num_failures += 1; | ||
"FAIL" | ||
}, | ||
_ => unreachable!("Unknown change severity"), | ||
}; | ||
table.add_row(row![change, check.code, check.description]); | ||
} | ||
|
||
eprintln!("{}", table); | ||
|
||
match num_failures { | ||
0 => Ok(RoverStdout::None), | ||
1 => Err(anyhow::anyhow!("Encountered 1 failure.")), | ||
_ => Err(anyhow::anyhow!("Encountered {} failures.", num_failures)) | ||
} | ||
} | ||
} |