Skip to content

Commit 08e6b6c

Browse files
committed
Add ValidationOptions structure to pass in recursion limit
Possibly also a diagnostic limit in the future. Maybe there would be options that could differ between executable and schema validation, but not right now
1 parent c7e90f0 commit 08e6b6c

30 files changed

+240
-136
lines changed

crates/apollo-compiler/README.md

+12-7
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ Older version may or may not be compatible.
5353
You can get started with `apollo-compiler`:
5454
```rust
5555
use apollo_compiler::Schema;
56+
use apollo_compiler::validation::ValidationOptions;
5657

5758
let input = r#"
5859
interface Pet {
@@ -87,14 +88,15 @@ let schema = Schema::parse(input, "document.graphql");
8788

8889
/// In case of validation errors, the panic message will be nicely formatted
8990
/// to point at relevant parts of the source file(s)
90-
schema.validate().unwrap();
91+
schema.validate(ValidationOptions::default()).unwrap();
9192
```
9293

9394
### Examples
9495
#### Accessing fragment definition field types
9596

9697
```rust
9798
use apollo_compiler::{Schema, ExecutableDocument, Node, executable};
99+
use apollo_compiler::validation::ValidationOptions;
98100

99101
fn main() {
100102
let schema_input = r#"
@@ -124,8 +126,8 @@ fn main() {
124126
let schema = Schema::parse(schema_input, "schema.graphql");
125127
let document = ExecutableDocument::parse(&schema, query_input, "query.graphql");
126128

127-
schema.validate().unwrap();
128-
document.validate(&schema).unwrap();
129+
schema.validate(ValidationOptions::default()).unwrap();
130+
document.validate(&schema, ValidationOptions::default()).unwrap();
129131

130132
let op = document.get_operation(Some("getUser")).expect("getUser query does not exist");
131133
let fragment_in_op = op.selection_set.selections.iter().filter_map(|sel| match sel {
@@ -149,6 +151,7 @@ fn main() {
149151
#### Get a directive defined on a field used in a query operation definition.
150152
```rust
151153
use apollo_compiler::{Schema, ExecutableDocument, Node, executable};
154+
use apollo_compiler::validation::ValidationOptions;
152155

153156
fn main() {
154157
let schema_input = r#"
@@ -187,8 +190,8 @@ fn main() {
187190
let schema = Schema::parse(schema_input, "schema.graphql");
188191
let document = ExecutableDocument::parse(&schema, query_input, "query.graphql");
189192

190-
schema.validate().unwrap();
191-
document.validate(&schema).unwrap();
193+
schema.validate(ValidationOptions::default()).unwrap();
194+
document.validate(&schema, ValidationOptions::default()).unwrap();
192195

193196
let get_product_op = document
194197
.get_operation(Some("getProduct"))
@@ -215,6 +218,8 @@ fn main() {
215218

216219
#### Printing diagnostics for a faulty GraphQL document
217220
```rust
221+
use apollo_compiler::validation::ValidationOptions;
222+
218223
let input = r#"
219224
query {
220225
cat {
@@ -273,10 +278,10 @@ union CatOrDog = Cat | Dog
273278

274279
let (schema, executable) = apollo_compiler::parse_mixed(input, "document.graphql");
275280

276-
if let Err(diagnostics) = schema.validate() {
281+
if let Err(diagnostics) = schema.validate(ValidationOptions::default()) {
277282
println!("{diagnostics}")
278283
}
279-
if let Err(diagnostics) = executable.validate(&schema) {
284+
if let Err(diagnostics) = executable.validate(&schema, ValidationOptions::default()) {
280285
println!("{diagnostics}")
281286
}
282287
```

crates/apollo-compiler/examples/file_watcher.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -118,10 +118,10 @@ impl FileWatcher {
118118
}
119119

120120
fn validate(&self, (schema, executable): &(Schema, ExecutableDocument)) {
121-
if let Err(err) = schema.validate() {
121+
if let Err(err) = schema.validate(Default::default()) {
122122
println!("{err}")
123123
}
124-
if let Err(err) = executable.validate(schema) {
124+
if let Err(err) = executable.validate(schema, Default::default()) {
125125
println!("{err}")
126126
}
127127
}

crates/apollo-compiler/examples/multi_source_validation.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ fn compile_from_dir() -> io::Result<()> {
2020
let entry = entry?;
2121
let src = fs::read_to_string(entry.path()).expect("Could not read document file.");
2222
let (schema, executable) = parse_mixed(&src, entry.path());
23-
schema.validate().unwrap();
24-
executable.validate(&schema).unwrap();
23+
schema.validate(Default::default()).unwrap();
24+
executable.validate(&schema, Default::default()).unwrap();
2525
}
2626
}
2727
Ok(())
@@ -42,14 +42,14 @@ fn compile_schema_and_query_files() -> io::Result<()> {
4242
.parse(src, schema)
4343
.parse(src_ext, schema_ext)
4444
.build();
45-
schema.validate().unwrap();
45+
schema.validate(Default::default()).unwrap();
4646

4747
// get_dog_name is a query-only file
4848
let query = Path::new("crates/apollo-compiler/examples/documents/get_dog_name.graphql");
4949
let src = fs::read_to_string(query).expect("Could not read query file.");
5050
let doc = ExecutableDocument::parse(&schema, src, query);
5151

52-
doc.validate(&schema).unwrap();
52+
doc.validate(&schema, Default::default()).unwrap();
5353

5454
Ok(())
5555
}
@@ -127,9 +127,9 @@ query getDogName {
127127
}
128128
"#;
129129
let schema = Schema::parse(schema, "schema.graphl");
130-
schema.validate().unwrap();
130+
schema.validate(Default::default()).unwrap();
131131
let doc = ExecutableDocument::parse(&schema, query, "query.graphql");
132-
doc.validate(&schema).unwrap();
132+
doc.validate(&schema, Default::default()).unwrap();
133133

134134
Ok(())
135135
}

crates/apollo-compiler/examples/rename.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ fn main() {
1212
fn renamed() -> Schema {
1313
let input = "type Query { field: Int }";
1414
let mut schema = Schema::parse(input, "schema.graphql");
15-
schema.validate().unwrap();
15+
schema.validate(Default::default()).unwrap();
1616

1717
// 1. Remove the definition from the `types` map, using its old name as a key
1818
let mut type_def = schema.types.remove("Query").unwrap();
@@ -38,7 +38,7 @@ fn renamed() -> Schema {
3838
.unwrap()
3939
.name = new_name;
4040

41-
schema.validate().unwrap();
41+
schema.validate(Default::default()).unwrap();
4242
schema
4343
}
4444

crates/apollo-compiler/examples/timed.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ fn main() -> ExitCode {
2424
let step = format!("Schema parse ({} bytes)", schema_source.len());
2525
let schema = timed(&step, || Schema::parse(schema_source, schema_filename));
2626

27-
if let Err(errors) = timed("Schema validation", || schema.validate()) {
27+
if let Err(errors) = timed("Schema validation", || schema.validate(Default::default())) {
2828
println!("Schema is invalid:\n{errors}")
2929
}
3030

@@ -36,7 +36,9 @@ fn main() -> ExitCode {
3636
ExecutableDocument::parse(&schema, executable_source, executable_filename)
3737
});
3838

39-
if let Err(errors) = timed("Executable document validation", || doc.validate(&schema)) {
39+
if let Err(errors) = timed("Executable document validation", || {
40+
doc.validate(&schema, Default::default())
41+
}) {
4042
println!("Executable document is invalid:\n{errors}")
4143
}
4244

crates/apollo-compiler/examples/validate.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ fn main() {
1919
};
2020

2121
let (schema, executable) = apollo_compiler::parse_mixed(source, filename);
22-
let schema_result = schema.validate();
23-
let executable_result = executable.validate(&schema);
22+
let schema_result = schema.validate(Default::default());
23+
let executable_result = executable.validate(&schema, Default::default());
2424
let has_errors = schema_result.is_err() || executable_result.is_err();
2525
match schema_result {
2626
Ok(warnings) => println!("{warnings}"),

crates/apollo-compiler/src/ast/impls.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use crate::schema::ComponentName;
55
use crate::schema::ComponentOrigin;
66
use crate::schema::SchemaBuilder;
77
use crate::validation::DiagnosticList;
8+
use crate::validation::ValidationOptions;
89
use crate::ExecutableDocument;
910
use crate::Parser;
1011
use crate::Schema;
@@ -47,15 +48,22 @@ impl Document {
4748
}
4849

4950
/// Validate as an executable document, as much as possible without a schema
50-
pub fn validate_standalone_executable(&self) -> Result<(), DiagnosticList> {
51+
pub fn validate_standalone_executable(
52+
&self,
53+
options: ValidationOptions,
54+
) -> Result<(), DiagnosticList> {
5155
let type_system_definitions_are_errors = true;
5256
let executable = crate::executable::from_ast::document_from_ast(
5357
None,
5458
self,
5559
type_system_definitions_are_errors,
5660
);
5761
let mut errors = DiagnosticList::new(None, self.sources.clone());
58-
crate::executable::validation::validate_standalone_executable(&mut errors, &executable);
62+
crate::executable::validation::validate_standalone_executable(
63+
&mut errors,
64+
&executable,
65+
options,
66+
);
5967
errors.into_result()
6068
}
6169

crates/apollo-compiler/src/database/inputs.rs

+3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ pub(crate) trait InputDatabase {
77
#[salsa::input]
88
fn input(&self, file_id: FileId) -> Source;
99

10+
#[salsa::input]
11+
fn recursion_limit(&self) -> usize;
12+
1013
#[salsa::input]
1114
fn schema_input(&self) -> Option<Arc<crate::Schema>>;
1215

crates/apollo-compiler/src/executable/mod.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ pub use crate::ast::{
2020
VariableDefinition,
2121
};
2222
use crate::validation::DiagnosticList;
23+
use crate::validation::ValidationOptions;
2324
use crate::NodeLocation;
2425
use std::fmt;
2526

@@ -227,9 +228,13 @@ impl ExecutableDocument {
227228
Parser::new().parse_executable(schema, source_text, path)
228229
}
229230

230-
pub fn validate(&self, schema: &Schema) -> Result<(), DiagnosticList> {
231+
pub fn validate(
232+
&self,
233+
schema: &Schema,
234+
options: ValidationOptions,
235+
) -> Result<(), DiagnosticList> {
231236
let mut errors = DiagnosticList::new(Some(schema.sources.clone()), self.sources.clone());
232-
validation::validate_executable_document(&mut errors, schema, self);
237+
validation::validate_executable_document(&mut errors, schema, self, options);
233238
errors.into_result()
234239
}
235240

crates/apollo-compiler/src/executable/validation.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use super::FieldSet;
33
use crate::ast;
44
use crate::validation::Details;
55
use crate::validation::DiagnosticList;
6+
use crate::validation::ValidationOptions;
67
use crate::ExecutableDocument;
78
use crate::FileId;
89
use crate::InputDatabase;
@@ -14,18 +15,20 @@ pub(crate) fn validate_executable_document(
1415
errors: &mut DiagnosticList,
1516
schema: &Schema,
1617
document: &ExecutableDocument,
18+
options: ValidationOptions,
1719
) {
1820
validate_common(errors, document);
19-
compiler_validation(errors, Some(schema), document);
21+
compiler_validation(errors, Some(schema), document, options);
2022
// TODO
2123
}
2224

2325
pub(crate) fn validate_standalone_executable(
2426
errors: &mut DiagnosticList,
2527
document: &ExecutableDocument,
28+
options: ValidationOptions,
2629
) {
2730
validate_common(errors, document);
28-
compiler_validation(errors, None, document);
31+
compiler_validation(errors, None, document, options);
2932
}
3033

3134
pub(crate) fn validate_common(errors: &mut DiagnosticList, document: &ExecutableDocument) {
@@ -75,6 +78,7 @@ fn compiler_validation(
7578
errors: &mut DiagnosticList,
7679
schema: Option<&Schema>,
7780
document: &ExecutableDocument,
81+
options: ValidationOptions,
7882
) {
7983
let mut compiler = crate::ApolloCompiler::new();
8084
let mut ids = Vec::new();
@@ -93,6 +97,8 @@ fn compiler_validation(
9397
compiler.db.set_schema_input(Some(Arc::new(schema.clone())));
9498
}
9599

100+
compiler.db.set_recursion_limit(options.recursion_limit);
101+
96102
let ast_id = FileId::HACK_TMP;
97103
ids.push(ast_id);
98104
let ast = document.to_ast();

crates/apollo-compiler/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ mod node;
1010
mod node_str;
1111
mod parser;
1212
pub mod schema;
13-
mod validation;
13+
pub mod validation;
1414

1515
use crate::database::{InputDatabase, ReprDatabase, RootDatabase, Source};
1616
use crate::diagnostics::ApolloDiagnostic;

crates/apollo-compiler/src/schema/mod.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! High-level representation of a GraphQL schema
22
33
use crate::ast;
4+
use crate::validation::ValidationOptions;
45
use crate::FileId;
56
use crate::Node;
67
use crate::NodeLocation;
@@ -313,9 +314,9 @@ impl Schema {
313314
}
314315

315316
/// Returns `Err` if invalid, or `Ok` for potential warnings or advice
316-
pub fn validate(&self) -> Result<DiagnosticList, DiagnosticList> {
317+
pub fn validate(&self, options: ValidationOptions) -> Result<DiagnosticList, DiagnosticList> {
317318
let mut errors = DiagnosticList::new(None, self.sources.clone());
318-
let warnings_and_advice = validation::validate_schema(&mut errors, self);
319+
let warnings_and_advice = validation::validate_schema(&mut errors, self, options);
319320
let valid = errors.is_empty();
320321
for diagnostic in warnings_and_advice {
321322
errors.push(

crates/apollo-compiler/src/schema/validation.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use super::BuildError;
22
use crate::validation::Details;
33
use crate::validation::DiagnosticList;
4+
use crate::validation::ValidationOptions;
45
use crate::FileId;
56
use crate::InputDatabase;
67
use crate::Schema;
@@ -10,14 +11,15 @@ use std::sync::Arc;
1011
pub(crate) fn validate_schema(
1112
errors: &mut DiagnosticList,
1213
schema: &Schema,
14+
options: ValidationOptions,
1315
) -> Vec<crate::ApolloDiagnostic> {
1416
for (&file_id, source) in schema.sources.iter() {
1517
source.validate_parse_errors(errors, file_id)
1618
}
1719
for build_error in &schema.build_errors {
1820
validate_build_error(errors, build_error)
1921
}
20-
compiler_validation(errors, schema)
22+
compiler_validation(errors, schema, options)
2123
}
2224

2325
fn validate_build_error(errors: &mut DiagnosticList, build_error: &BuildError) {
@@ -47,6 +49,7 @@ fn validate_build_error(errors: &mut DiagnosticList, build_error: &BuildError) {
4749
fn compiler_validation(
4850
errors: &mut DiagnosticList,
4951
schema: &Schema,
52+
options: ValidationOptions,
5053
) -> Vec<crate::ApolloDiagnostic> {
5154
let mut compiler = crate::ApolloCompiler::new();
5255
let mut ids = Vec::new();
@@ -68,6 +71,7 @@ fn compiler_validation(
6871
},
6972
);
7073
compiler.db.set_source_files(ids);
74+
compiler.db.set_recursion_limit(options.recursion_limit);
7175
let mut warnings_and_advice = Vec::new();
7276
for diagnostic in compiler.db.validate_type_system() {
7377
if diagnostic.data.is_error() {

crates/apollo-compiler/src/validation/directive.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,10 @@ impl FindRecursiveDirective<'_> {
127127
fn check(
128128
schema: &schema::Schema,
129129
directive_def: &Node<ast::DirectiveDefinition>,
130+
recursion_limit: usize,
130131
) -> Result<(), CycleError<ast::Directive>> {
131-
let mut recursion_stack = RecursionStack::with_root(directive_def.name.clone(), 500);
132+
let mut recursion_stack =
133+
RecursionStack::with_root(directive_def.name.clone(), recursion_limit);
132134
FindRecursiveDirective { schema }
133135
.directive_definition(recursion_stack.guard(), directive_def)
134136
}
@@ -150,7 +152,7 @@ pub(crate) fn validate_directive_definition(
150152
// references itself directly.
151153
//
152154
// Returns Recursive Definition error.
153-
if let Err(error) = FindRecursiveDirective::check(&db.schema(), &def) {
155+
if let Err(error) = FindRecursiveDirective::check(&db.schema(), &def, db.recursion_limit()) {
154156
let definition_location = def.location();
155157
let head_location = NodeLocation::recompose(def.location(), def.name.location());
156158
let mut diagnostic = ApolloDiagnostic::new(

crates/apollo-compiler/src/validation/fragment.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,7 @@ pub(crate) fn validate_fragment_cycles(
336336
Ok(())
337337
}
338338

339-
let mut visited = RecursionStack::with_root(def.name.clone(), 500);
339+
let mut visited = RecursionStack::with_root(def.name.clone(), db.recursion_limit());
340340

341341
if let Err(cycle) =
342342
detect_fragment_cycles(&named_fragments, &def.selection_set, &mut visited.guard())

0 commit comments

Comments
 (0)