Skip to content

Commit 3cb9bf4

Browse files
committed
Added NoUndefinedVariables
1 parent 0a647d8 commit 3cb9bf4

File tree

7 files changed

+676
-6
lines changed

7 files changed

+676
-6
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ cargo add graphql-tools
5757
- [x] PossibleFragmentSpreads
5858
- [x] NoFragmentCycles
5959
- [ ] UniqueVariableNames
60-
- [ ] NoUndefinedVariables
60+
- [x] NoUndefinedVariables
6161
- [ ] NoUnusedVariables
6262
- [ ] KnownDirectives
6363
- [ ] UniqueDirectivesPerLocation

src/ast/ext.rs

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,94 @@
1-
use crate::static_graphql::query::{self, FragmentSpread};
1+
use std::collections::{HashMap, HashSet};
2+
3+
use crate::ast::QueryVisitor;
4+
use crate::static_graphql::query::{self, FragmentSpread, OperationDefinition};
25
use crate::static_graphql::schema::{
36
self, Field, InterfaceType, ObjectType, TypeDefinition, UnionType,
47
};
58

69
use super::{get_named_type, TypeInfoElementRef, TypeInfoRegistry};
710

11+
pub trait AstWithVariables {
12+
fn get_variables(&self) -> Vec<query::VariableDefinition>;
13+
fn get_variables_in_use(
14+
&self,
15+
fragments: &HashMap<String, query::FragmentDefinition>,
16+
) -> HashSet<String>;
17+
}
18+
19+
impl AstWithVariables for OperationDefinition {
20+
fn get_variables(&self) -> Vec<query::VariableDefinition> {
21+
match self {
22+
OperationDefinition::Query(query) => query.variable_definitions.clone(),
23+
OperationDefinition::SelectionSet(_anon_query) => vec![],
24+
OperationDefinition::Mutation(mutation) => mutation.variable_definitions.clone(),
25+
OperationDefinition::Subscription(subscription) => {
26+
subscription.variable_definitions.clone()
27+
}
28+
}
29+
}
30+
31+
fn get_variables_in_use(
32+
&self,
33+
fragments: &HashMap<String, query::FragmentDefinition>,
34+
) -> HashSet<String> {
35+
struct GetVariablesInUse;
36+
37+
struct GetVariablesInUseHelper<'a> {
38+
variables_in_use: HashSet<String>,
39+
available_fragments: &'a HashMap<String, query::FragmentDefinition>,
40+
visited_fragments: HashSet<String>,
41+
}
42+
43+
impl<'a> QueryVisitor<GetVariablesInUseHelper<'a>> for GetVariablesInUse {
44+
fn enter_fragment_spread(
45+
&self,
46+
_node: &FragmentSpread,
47+
_visitor_context: &mut GetVariablesInUseHelper,
48+
) {
49+
if !_visitor_context
50+
.visited_fragments
51+
.contains(&_node.fragment_name)
52+
{
53+
_visitor_context
54+
.visited_fragments
55+
.insert(_node.fragment_name.clone());
56+
57+
if let Some(fragment_def) = _visitor_context
58+
.available_fragments
59+
.get(&_node.fragment_name)
60+
{
61+
self.__visit_selection_set(&fragment_def.selection_set, _visitor_context);
62+
}
63+
}
64+
}
65+
66+
fn enter_variable(
67+
&self,
68+
_name: &String,
69+
_parent_arg: (&String, &query::Value),
70+
_parent_field: &query::Field,
71+
_visitor_context: &mut GetVariablesInUseHelper,
72+
) {
73+
_visitor_context.variables_in_use.insert(_name.clone());
74+
}
75+
}
76+
77+
let visitor = GetVariablesInUse {};
78+
let doc = query::Document {
79+
definitions: vec![query::Definition::Operation(self.clone())],
80+
};
81+
let mut helper = GetVariablesInUseHelper {
82+
variables_in_use: HashSet::new(),
83+
available_fragments: fragments,
84+
visited_fragments: HashSet::new(),
85+
};
86+
visitor.visit_document(&doc, &mut helper);
87+
88+
helper.variables_in_use
89+
}
90+
}
91+
892
pub trait AstNodeWithFields {
993
fn find_field(&self, name: String) -> Option<&Field>;
1094
}

src/ast/query_visitor.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,29 @@ pub trait QueryVisitor<T = DefaultVisitorContext> {
111111

112112
for (name, argument) in &field.arguments {
113113
self.enter_field_argument(name, argument, field, visitor_context);
114+
115+
println!("args: {}", name);
116+
117+
match argument {
118+
Value::Variable(variable) => {
119+
println!("variable: {}", argument);
120+
121+
self.enter_variable(
122+
variable,
123+
(name, argument),
124+
&field,
125+
visitor_context,
126+
);
127+
self.leave_variable(
128+
variable,
129+
(name, argument),
130+
&field,
131+
visitor_context,
132+
);
133+
}
134+
_ => {}
135+
}
136+
114137
self.leave_field_argument(name, argument, field, visitor_context);
115138
}
116139

@@ -196,6 +219,23 @@ pub trait QueryVisitor<T = DefaultVisitorContext> {
196219
) {
197220
}
198221

222+
fn enter_variable(
223+
&self,
224+
_name: &String,
225+
_parent_arg: (&String, &Value),
226+
_parent_field: &Field,
227+
_visitor_context: &mut T,
228+
) {
229+
}
230+
fn leave_variable(
231+
&self,
232+
_name: &String,
233+
_parent_arg: (&String, &Value),
234+
_parent_field: &Field,
235+
_visitor_context: &mut T,
236+
) {
237+
}
238+
199239
fn enter_fragment_spread(&self, _node: &FragmentSpread, _visitor_context: &mut T) {}
200240
fn leave_fragment_spread(&self, _node: &FragmentSpread, _visitor_context: &mut T) {}
201241

src/ast/type_info_query_visitor.rs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use super::{
33
TypeInfoRegistry,
44
};
55
use crate::static_graphql::{
6-
query::{self, Type},
6+
query::{self, Field, Type, Value},
77
schema::{self},
88
};
99

@@ -270,6 +270,27 @@ pub trait TypeInfoQueryVisitor<T = DefaultVisitorContext> {
270270
visitor_context,
271271
type_info,
272272
);
273+
274+
match argument_type {
275+
Value::Variable(variable) => {
276+
self.enter_variable(
277+
variable,
278+
(argument_name, argument_type),
279+
&field,
280+
visitor_context,
281+
type_info,
282+
);
283+
self.leave_variable(
284+
variable,
285+
(argument_name, argument_type),
286+
&field,
287+
visitor_context,
288+
type_info,
289+
);
290+
}
291+
_ => {}
292+
}
293+
273294
self.leave_field_argument(
274295
argument_name,
275296
argument_type,
@@ -487,6 +508,25 @@ pub trait TypeInfoQueryVisitor<T = DefaultVisitorContext> {
487508
) {
488509
}
489510

511+
fn enter_variable(
512+
&self,
513+
_name: &String,
514+
_parent_arg: (&String, &Value),
515+
_parent_field: &Field,
516+
_visitor_context: &mut T,
517+
_type_info: &TypeInfo,
518+
) {
519+
}
520+
fn leave_variable(
521+
&self,
522+
_name: &String,
523+
_parent_arg: (&String, &Value),
524+
_parent_field: &Field,
525+
_visitor_context: &mut T,
526+
_type_info: &TypeInfo,
527+
) {
528+
}
529+
490530
fn enter_fragment_spread(
491531
&self,
492532
_node: &query::FragmentSpread,

src/validation/rules/defaults.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ use crate::validation::validate::ValidationPlan;
22

33
use super::{
44
FieldsOnCorrectType, FragmentsOnCompositeTypes, KnownFragmentNames, KnownTypeNames,
5-
LeafFieldSelections, LoneAnonymousOperation, NoFragmentsCycle, NoUnusedFragments,
6-
OverlappingFieldsCanBeMerged, PossibleFragmentSpreads, SingleFieldSubscriptions,
7-
UniqueFragmentNames, UniqueOperationNames, VariablesAreInputTypes,
5+
LeafFieldSelections, LoneAnonymousOperation, NoFragmentsCycle, NoUndefinedVariables,
6+
NoUnusedFragments, OverlappingFieldsCanBeMerged, PossibleFragmentSpreads,
7+
SingleFieldSubscriptions, UniqueFragmentNames, UniqueOperationNames, VariablesAreInputTypes,
88
};
99

1010
pub fn default_rules_validation_plan() -> ValidationPlan {
@@ -24,6 +24,7 @@ pub fn default_rules_validation_plan() -> ValidationPlan {
2424
plan.add_rule(Box::new(OverlappingFieldsCanBeMerged {}));
2525
plan.add_rule(Box::new(NoFragmentsCycle {}));
2626
plan.add_rule(Box::new(PossibleFragmentSpreads {}));
27+
plan.add_rule(Box::new(NoUndefinedVariables {}));
2728

2829
plan
2930
}

src/validation/rules/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ pub mod known_type_names;
66
pub mod leaf_field_selections;
77
pub mod lone_anonymous_operation;
88
pub mod no_fragments_cycle;
9+
pub mod no_undefined_variables;
910
pub mod no_unused_fragments;
1011
pub mod overlapping_fields_can_be_merged;
1112
pub mod possible_fragment_spreads;
@@ -24,6 +25,7 @@ pub use self::known_type_names::*;
2425
pub use self::leaf_field_selections::*;
2526
pub use self::lone_anonymous_operation::*;
2627
pub use self::no_fragments_cycle::*;
28+
pub use self::no_undefined_variables::*;
2729
pub use self::no_unused_fragments::*;
2830
pub use self::overlapping_fields_can_be_merged::*;
2931
pub use self::possible_fragment_spreads::*;

0 commit comments

Comments
 (0)