Skip to content

Commit b226303

Browse files
committed
added a new validation phase
fixes update to latest run validations as soon as possible use specified rules instead of default set cleanup fix fmt added more rules move validation check to Query struct Added Leaf-Field-Selections rule fixes fixes Added an option to disable graphql validations: `DISABLE_GRAPHQL_VALIDATIONS`
1 parent 6cf64b8 commit b226303

File tree

11 files changed

+91
-8
lines changed

11 files changed

+91
-8
lines changed

Cargo.lock

Lines changed: 26 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
@@ -22,6 +22,7 @@ http = "0.2.3"
2222
fail = { version = "0.5", features = ["failpoints"] }
2323
futures = "0.1.21"
2424
graphql-parser = "0.4.0"
25+
graphql-tools = "0.0.6"
2526
lazy_static = "1.4.0"
2627
mockall = "0.8.3"
2728
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),
@@ -137,6 +138,7 @@ impl QueryExecutionError {
137138
| DeploymentReverted
138139
| SubgraphManifestResolveError(_)
139140
| InvalidSubgraphManifest
141+
| ValidationError(_, _)
140142
| ResultTooBig(_, _) => false,
141143
}
142144
}
@@ -161,6 +163,9 @@ impl fmt::Display for QueryExecutionError {
161163
OperationNotFound(s) => {
162164
write!(f, "Operation name not found `{}`", s)
163165
}
166+
ValidationError(_pos, message) => {
167+
write!(f, "{}", message)
168+
}
164169
NotSupported(s) => write!(f, "Not supported: {}", s),
165170
NoRootSubscriptionObjectType => {
166171
write!(f, "No root Subscription type defined in the schema")

graphql/Cargo.toml

Lines changed: 1 addition & 0 deletions
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.8"
1112
indexmap = "1.7"
1213
Inflector = "0.11.3"
1314
lazy_static = "1.2.0"

graphql/src/execution/query.rs

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use graphql_parser::Pos;
2+
use graphql_tools::validation::validate::{validate, ValidationPlan};
23
use std::collections::{HashMap, HashSet};
34
use std::hash::{Hash, Hasher};
45
use std::sync::Arc;
@@ -9,11 +10,10 @@ use graph::data::graphql::{
910
ext::{DocumentExt, TypeExt},
1011
ObjectOrInterface,
1112
};
13+
use graph::data::query::QueryExecutionError;
1214
use graph::data::query::{Query as GraphDataQuery, QueryVariables};
1315
use graph::data::schema::ApiSchema;
14-
use graph::prelude::{
15-
info, o, q, r, s, BlockNumber, CheapClone, Logger, QueryExecutionError, TryFromValue,
16-
};
16+
use graph::prelude::{info, o, q, r, s, BlockNumber, CheapClone, Logger, TryFromValue};
1717

1818
use crate::introspection::introspection_schema;
1919
use crate::query::{ast as qast, ext::BlockConstraint};
@@ -112,9 +112,24 @@ impl Query {
112112
schema: Arc<ApiSchema>,
113113
network: Option<String>,
114114
query: GraphDataQuery,
115+
validation_plan: Arc<ValidationPlan>,
115116
max_complexity: Option<u64>,
116117
max_depth: u8,
117118
) -> Result<Arc<Self>, Vec<QueryExecutionError>> {
119+
let validation_errors = validate(&schema.document(), &query.document, &validation_plan);
120+
121+
if validation_errors.len() > 0 {
122+
return Err(validation_errors
123+
.into_iter()
124+
.map(|e| {
125+
QueryExecutionError::ValidationError(
126+
e.locations.clone().into_iter().nth(0),
127+
e.message,
128+
)
129+
})
130+
.collect());
131+
}
132+
118133
let mut operation = None;
119134
let mut fragments = HashMap::new();
120135
for defn in query.document.definitions.into_iter() {

graphql/src/runner.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ use graph::{
2121
data::query::{QueryResults, QueryTarget},
2222
prelude::QueryStore,
2323
};
24+
use graphql_tools::validation::rules::{
25+
FragmentsOnCompositeTypes, KnownFragmentNamesRule, LeafFieldSelections, LoneAnonymousOperation,
26+
NoUnusedFragments, OverlappingFieldsCanBeMerged,
27+
};
28+
use graphql_tools::validation::validate::ValidationPlan;
2429

2530
use lazy_static::lazy_static;
2631

@@ -77,6 +82,7 @@ pub struct GraphQlRunner<S, SM> {
7782
subscription_manager: Arc<SM>,
7883
load_manager: Arc<LoadManager>,
7984
result_size: Arc<ResultSizeMetrics>,
85+
pub graphql_validation_plan: Arc<ValidationPlan>,
8086
}
8187

8288
lazy_static! {
@@ -86,6 +92,10 @@ lazy_static! {
8692
u64::from_str(&s)
8793
.unwrap_or_else(|_| panic!("failed to parse env var GRAPH_GRAPHQL_QUERY_TIMEOUT"))
8894
));
95+
static ref DISABLE_GRAPHQL_VALIDATIONS: bool = std::env::var("DISABLE_GRAPHQL_VALIDATIONS")
96+
.unwrap_or_else(|_| "false".into())
97+
.parse::<bool>()
98+
.unwrap_or_else(|_| false);
8999
static ref GRAPHQL_MAX_COMPLEXITY: Option<u64> = env::var("GRAPH_GRAPHQL_MAX_COMPLEXITY")
90100
.ok()
91101
.map(|s| u64::from_str(&s)
@@ -135,12 +145,24 @@ where
135145
) -> Self {
136146
let logger = logger.new(o!("component" => "GraphQlRunner"));
137147
let result_size = Arc::new(ResultSizeMetrics::new(registry));
148+
let mut graphql_validation_plan = ValidationPlan { rules: Vec::new() };
149+
150+
if !(*DISABLE_GRAPHQL_VALIDATIONS) {
151+
graphql_validation_plan.add_rule(Box::new(LoneAnonymousOperation {}));
152+
graphql_validation_plan.add_rule(Box::new(FragmentsOnCompositeTypes {}));
153+
graphql_validation_plan.add_rule(Box::new(OverlappingFieldsCanBeMerged {}));
154+
graphql_validation_plan.add_rule(Box::new(KnownFragmentNamesRule {}));
155+
graphql_validation_plan.add_rule(Box::new(NoUnusedFragments {}));
156+
graphql_validation_plan.add_rule(Box::new(LeafFieldSelections {}));
157+
}
158+
138159
GraphQlRunner {
139160
logger,
140161
store,
141162
subscription_manager,
142163
load_manager,
143164
result_size,
165+
graphql_validation_plan: Arc::new(graphql_validation_plan),
144166
}
145167
}
146168

@@ -212,6 +234,7 @@ where
212234
schema,
213235
network,
214236
query,
237+
self.graphql_validation_plan.clone(),
215238
max_complexity,
216239
max_depth,
217240
)?;
@@ -317,6 +340,7 @@ where
317340
schema,
318341
Some(network.clone()),
319342
subscription.query,
343+
self.graphql_validation_plan.clone(),
320344
*GRAPHQL_MAX_COMPLEXITY,
321345
*GRAPHQL_MAX_DEPTH,
322346
)?;

graphql/src/subscription/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use std::time::{Duration, Instant};
55

66
use graph::components::store::UnitStream;
77
use graph::{components::store::SubscriptionManager, prelude::*};
8+
use graphql_tools::validation::validate::ValidationPlan;
89

910
use crate::runner::ResultSizeMetrics;
1011
use crate::{
@@ -51,6 +52,7 @@ pub fn execute_subscription(
5152
schema,
5253
None,
5354
subscription.query,
55+
Arc::new(ValidationPlan { rules: vec![] }),
5456
options.max_complexity,
5557
options.max_depth,
5658
)?;

server/index-node/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,4 @@ http = "0.2"
1414
hyper = "0.14"
1515
serde = "1.0"
1616
either = "1.6.1"
17+
graphql-tools = "0.0.8"

server/index-node/src/service.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use graphql_tools::validation::validate::ValidationPlan;
12
use http::header::{
23
self, ACCESS_CONTROL_ALLOW_HEADERS, ACCESS_CONTROL_ALLOW_METHODS, ACCESS_CONTROL_ALLOW_ORIGIN,
34
CONTENT_TYPE, LOCATION,
@@ -113,7 +114,15 @@ where
113114
.await?;
114115

115116
let query = IndexNodeRequest::new(body).compat().await?;
116-
let query = match PreparedQuery::new(&self.logger, schema, None, query, None, 100) {
117+
let query = match PreparedQuery::new(
118+
&self.logger,
119+
schema,
120+
None,
121+
query,
122+
Arc::new(ValidationPlan { rules: vec![] }),
123+
None,
124+
100,
125+
) {
117126
Ok(query) => query,
118127
Err(e) => return Ok(QueryResults::from(QueryResult::from(e)).as_http_response()),
119128
};

store/test-store/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@ lazy_static = "1.1"
1616
hex-literal = "0.3"
1717
diesel = { version = "1.4.8", features = ["postgres", "serde_json", "numeric", "r2d2"] }
1818
graph-chain-ethereum = { path = "../../chain/ethereum" }
19+
graphql-tools = "0.0.8"

store/test-store/src/store.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use graph_store_postgres::{
2424
BlockStore as DieselBlcokStore, DeploymentPlacer, SubgraphStore as DieselSubgraphStore,
2525
PRIMARY_SHARD,
2626
};
27+
use graphql_tools::validation::validate::ValidationPlan;
2728
use hex_literal::hex;
2829
use lazy_static::lazy_static;
2930
use std::collections::HashMap;
@@ -399,6 +400,7 @@ async fn execute_subgraph_query_internal(
399400
schema,
400401
network,
401402
query,
403+
Arc::new(ValidationPlan { rules: vec![] }),
402404
max_complexity,
403405
100
404406
));

0 commit comments

Comments
 (0)