Skip to content

Commit 7533fc1

Browse files
committed
added a new validation phase
1 parent 528fd19 commit 7533fc1

File tree

7 files changed

+57
-7
lines changed

7 files changed

+57
-7
lines changed

Cargo.lock

Lines changed: 15 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

graph/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ http = "0.2.3"
2121
fail = { version = "0.5", features = ["failpoints"] }
2222
futures = "0.1.21"
2323
graphql-parser = "0.4.0"
24+
graphql-tools = "0.0.4"
2425
lazy_static = "1.4.0"
2526
mockall = "0.8.3"
2627
num-bigint = { version = "^0.2.6", features = ["serde"] }

graph/src/data/query/error.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ pub enum QueryExecutionError {
4141
AbstractTypeError(String),
4242
InvalidArgumentError(Pos, String, q::Value),
4343
MissingArgumentError(Pos, String),
44+
ValidationError(Option<Pos>, String),
4445
InvalidVariableTypeError(Pos, String),
4546
MissingVariableError(Pos, String),
4647
ResolveEntitiesError(String),
@@ -135,6 +136,7 @@ impl QueryExecutionError {
135136
| DeploymentReverted
136137
| SubgraphManifestResolveError(_)
137138
| InvalidSubgraphManifest
139+
| ValidationError(_, _)
138140
| ResultTooBig(_, _) => false,
139141
}
140142
}
@@ -159,6 +161,9 @@ impl fmt::Display for QueryExecutionError {
159161
OperationNotFound(s) => {
160162
write!(f, "Operation name not found `{}`", s)
161163
}
164+
ValidationError(_pos, message) => {
165+
write!(f, "{}", message)
166+
}
162167
NotSupported(s) => write!(f, "Not supported: {}", s),
163168
NoRootSubscriptionObjectType => {
164169
write!(f, "No root Subscription type defined in the schema")

graph/src/data/query/result.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use serde::Serialize;
99
use std::collections::BTreeMap;
1010
use std::convert::TryFrom;
1111
use std::sync::Arc;
12+
use graphql_tools::validation::utils::{ValidationError};
1213

1314
fn serialize_data<S>(data: &Option<Data>, serializer: S) -> Result<S::Ok, S::Error>
1415
where
@@ -172,7 +173,7 @@ impl QueryResults {
172173
}
173174
}
174175

175-
/// The result of running a query, if successful.
176+
/// The result of running a GraphQL query.
176177
#[derive(Debug, Serialize)]
177178
pub struct QueryResult {
178179
#[serde(
@@ -182,6 +183,7 @@ pub struct QueryResult {
182183
data: Option<Data>,
183184
#[serde(skip_serializing_if = "Vec::is_empty")]
184185
errors: Vec<QueryError>,
186+
/// This is a non-standard GraphQL response field.
185187
#[serde(skip_serializing)]
186188
pub deployment: Option<DeploymentHash>,
187189
}
@@ -250,6 +252,20 @@ impl From<QueryExecutionError> for QueryResult {
250252
}
251253
}
252254

255+
impl From<Vec<ValidationError>> for QueryResult {
256+
fn from(errors: Vec<ValidationError>) -> Self {
257+
let execution_errors = errors.iter().map(|e| {
258+
QueryError::ExecutionError(QueryExecutionError::ValidationError(e.locations.clone().into_iter().nth(0), e.message.clone()))
259+
}).collect::<Vec<QueryError>>();
260+
261+
QueryResult {
262+
data: None,
263+
errors: execution_errors,
264+
deployment: None,
265+
}
266+
}
267+
}
268+
253269
impl From<QueryError> for QueryResult {
254270
fn from(e: QueryError) -> Self {
255271
QueryResult {

graphql/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ crossbeam = "0.8"
88
futures01 = { package="futures", version="0.1.29" }
99
graph = { path = "../graph" }
1010
graphql-parser = "0.4.0"
11+
graphql-tools = "0.0.4"
1112
indexmap = "1.7"
1213
Inflector = "0.11.3"
1314
lazy_static = "1.2.0"
@@ -20,4 +21,4 @@ anyhow = "1.0"
2021
[dev-dependencies]
2122
pretty_assertions = "1.0.0"
2223
test-store = { path = "../store/test-store" }
23-
graph-chain-ethereum = { path = "../chain/ethereum" }
24+
graph-chain-ethereum = { path = "../chain/ethereum" }

graphql/src/runner.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::time::{Duration, Instant};
77
use crate::prelude::{QueryExecutionOptions, StoreResolver, SubscriptionExecutionOptions};
88
use crate::query::execute_query;
99
use crate::subscription::execute_prepared_subscription;
10-
use graph::prelude::MetricsRegistry;
10+
use graph::prelude::{MetricsRegistry, QueryResult};
1111
use graph::prometheus::{Gauge, Histogram};
1212
use graph::{
1313
components::store::SubscriptionManager,
@@ -21,6 +21,8 @@ use graph::{
2121
data::query::{QueryResults, QueryTarget},
2222
prelude::QueryStore,
2323
};
24+
use graphql_tools::validation::validate::{validate, ValidationPlan};
25+
use graphql_tools::validation::rules::{OverlappingFieldsCanBeMerged};
2426

2527
use lazy_static::lazy_static;
2628

@@ -77,6 +79,7 @@ pub struct GraphQlRunner<S, SM> {
7779
subscription_manager: Arc<SM>,
7880
load_manager: Arc<LoadManager>,
7981
result_size: Arc<ResultSizeMetrics>,
82+
graphql_validation_plan: ValidationPlan,
8083
}
8184

8285
lazy_static! {
@@ -135,12 +138,16 @@ where
135138
) -> Self {
136139
let logger = logger.new(o!("component" => "GraphQlRunner"));
137140
let result_size = Arc::new(ResultSizeMetrics::new(registry));
141+
let mut graphql_validation_plan = ValidationPlan { rules: Vec::new() };
142+
graphql_validation_plan.add_rule(Box::new(OverlappingFieldsCanBeMerged {}));
143+
138144
GraphQlRunner {
139145
logger,
140146
store,
141147
subscription_manager,
142148
load_manager,
143149
result_size,
150+
graphql_validation_plan
144151
}
145152
}
146153

@@ -197,6 +204,11 @@ where
197204
let state = store.deployment_state().await?;
198205
let network = Some(store.network_name().to_string());
199206
let schema = store.api_schema()?;
207+
let validation_errors = validate(&schema.document(), &query.document, &self.graphql_validation_plan);
208+
209+
if validation_errors.len() > 0 {
210+
return Ok(QueryResults::from(QueryResult::from(validation_errors)));
211+
}
200212

201213
// Test only, see c435c25decbc4ad7bbbadf8e0ced0ff2
202214
#[cfg(debug_assertions)]

node/src/manager/commands/query.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ pub async fn run(
3131
};
3232

3333
let document = graphql_parser::parse_query(&query)?.into_static();
34+
35+
// Ideally, `validate` phase should happen here, but we don't have teh schema at this point,
36+
// so we cann it in `graphql/runner.rs` instead, where we have the api schema loaded.
37+
3438
let vars: Vec<(String, r::Value)> = vars
3539
.into_iter()
3640
.map(|v| {

0 commit comments

Comments
 (0)