Skip to content

Commit 098824f

Browse files
committed
added KnownTypeNames
1 parent 7a42caf commit 098824f

File tree

5 files changed

+257
-6
lines changed

5 files changed

+257
-6
lines changed

src/ast/type_info_query_visitor.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -385,15 +385,15 @@ pub trait TypeInfoQueryVisitor<T = DefaultVisitorContext> {
385385
&self,
386386
_node: &query::VariableDefinition,
387387
_parent_operation: &query::OperationDefinition,
388-
_visitor_context: &T,
388+
_visitor_context: &mut T,
389389
_type_info: &mut TypeInfo,
390390
) {
391391
}
392392
fn leave_variable_definition(
393393
&self,
394394
_node: &query::VariableDefinition,
395395
_parent_operation: &query::OperationDefinition,
396-
_visitor_context: &T,
396+
_visitor_context: &mut T,
397397
_type_info: &mut TypeInfo,
398398
) {
399399
}
@@ -433,7 +433,7 @@ pub trait TypeInfoQueryVisitor<T = DefaultVisitorContext> {
433433
_name: &String,
434434
_value: &query::Value,
435435
_parent_field: &query::Field,
436-
_visitor_context: &T,
436+
_visitor_context: &mut T,
437437
_type_info: &mut TypeInfo,
438438
) {
439439
}
@@ -442,7 +442,7 @@ pub trait TypeInfoQueryVisitor<T = DefaultVisitorContext> {
442442
_name: &String,
443443
_value: &query::Value,
444444
_parent_field: &query::Field,
445-
_visitor_context: &T,
445+
_visitor_context: &mut T,
446446
_type_info: &mut TypeInfo,
447447
) {
448448
}

src/validation/rules/defaults.rs

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

33
use super::{
4-
FieldsOnCorrectType, FragmentsOnCompositeTypes, KnownFragmentNamesRule, LeafFieldSelections,
5-
LoneAnonymousOperation, NoUnusedFragments, OverlappingFieldsCanBeMerged, UniqueOperationNames,
4+
FieldsOnCorrectType, FragmentsOnCompositeTypes, KnownFragmentNamesRule, KnownTypeNames,
5+
LeafFieldSelections, LoneAnonymousOperation, NoUnusedFragments, OverlappingFieldsCanBeMerged,
6+
UniqueOperationNames,
67
};
78

89
pub fn default_rules_validation_plan() -> ValidationPlan {
910
let mut plan = ValidationPlan { rules: vec![] };
1011

1112
plan.add_rule(Box::new(LoneAnonymousOperation {}));
13+
plan.add_rule(Box::new(KnownTypeNames {}));
1214
plan.add_rule(Box::new(FieldsOnCorrectType {}));
1315
plan.add_rule(Box::new(KnownFragmentNamesRule {}));
1416
plan.add_rule(Box::new(FragmentsOnCompositeTypes {}));
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
use super::ValidationRule;
2+
use crate::ast::ext::AstTypeRef;
3+
use crate::ast::TypeInfoQueryVisitor;
4+
use crate::static_graphql::query::TypeCondition;
5+
use crate::validation::utils::ValidationContext;
6+
use crate::validation::utils::{ValidationError, ValidationErrorContext};
7+
8+
/// Known type names
9+
///
10+
/// A GraphQL document is only valid if referenced types (specifically
11+
/// variable definitions and fragment conditions) are defined by the type schema.
12+
///
13+
/// See https://spec.graphql.org/draft/#sec-Fragment-Spread-Type-Existence
14+
pub struct KnownTypeNames;
15+
16+
impl<'a> TypeInfoQueryVisitor<ValidationErrorContext<'a>> for KnownTypeNames {
17+
fn enter_fragment_definition(
18+
&self,
19+
_node: &crate::static_graphql::query::FragmentDefinition,
20+
_visitor_context: &mut ValidationErrorContext<'a>,
21+
) {
22+
let TypeCondition::On(fragment_type_name) = &_node.type_condition;
23+
24+
if let None = _visitor_context
25+
.ctx
26+
.find_schema_definition_by_name(fragment_type_name.clone())
27+
{
28+
_visitor_context.errors.push(ValidationError {
29+
locations: vec![_node.position],
30+
message: format!("Unknown type \"{}\".", fragment_type_name),
31+
});
32+
}
33+
}
34+
35+
fn enter_inline_fragment(
36+
&self,
37+
_node: &crate::static_graphql::query::InlineFragment,
38+
_visitor_context: &mut ValidationErrorContext<'a>,
39+
_type_info: &mut crate::ast::TypeInfo,
40+
) {
41+
if let Some(TypeCondition::On(fragment_type_name)) = &_node.type_condition {
42+
if let None = _visitor_context
43+
.ctx
44+
.find_schema_definition_by_name(fragment_type_name.clone())
45+
{
46+
_visitor_context.errors.push(ValidationError {
47+
locations: vec![_node.position],
48+
message: format!("Unknown type \"{}\".", fragment_type_name),
49+
});
50+
}
51+
}
52+
}
53+
54+
fn enter_variable_definition(
55+
&self,
56+
_node: &crate::static_graphql::query::VariableDefinition,
57+
_parent_operation: &crate::static_graphql::query::OperationDefinition,
58+
_visitor_context: &mut ValidationErrorContext<'a>,
59+
_type_info: &mut crate::ast::TypeInfo,
60+
) {
61+
let base_type = _node.var_type.named_type();
62+
63+
if let None = _visitor_context
64+
.ctx
65+
.find_schema_definition_by_name(base_type.clone())
66+
{
67+
_visitor_context.errors.push(ValidationError {
68+
locations: vec![_node.position],
69+
message: format!("Unknown type \"{}\".", base_type),
70+
});
71+
}
72+
}
73+
}
74+
75+
impl ValidationRule for KnownTypeNames {
76+
fn validate<'a>(&self, ctx: &ValidationContext) -> Vec<ValidationError> {
77+
let mut error_context = ValidationErrorContext::new(ctx);
78+
79+
if let Some(type_info_registry) = &ctx.type_info_registry {
80+
self.visit_document(
81+
&ctx.operation.clone(),
82+
&mut error_context,
83+
&type_info_registry,
84+
);
85+
}
86+
87+
error_context.errors
88+
}
89+
}
90+
91+
#[test]
92+
fn known_type_names_are_valid() {
93+
use crate::validation::test_utils::*;
94+
95+
let mut plan = create_plan_from_rule(Box::new(KnownTypeNames {}));
96+
let errors = test_operation_with_schema(
97+
"
98+
query Foo(
99+
$var: String
100+
$required: [Int!]!
101+
$introspectionType: __EnumValue
102+
) {
103+
user(id: 4) {
104+
pets { ... on Pet { name }, ...PetFields, ... { name } }
105+
}
106+
}
107+
fragment PetFields on Pet {
108+
name
109+
}",
110+
&TEST_SCHEMA,
111+
&mut plan,
112+
);
113+
114+
let messages = get_messages(&errors);
115+
assert_eq!(messages.len(), 0);
116+
}
117+
118+
#[test]
119+
fn unknown_type_names_are_invalid() {
120+
use crate::validation::test_utils::*;
121+
122+
let mut plan = create_plan_from_rule(Box::new(KnownTypeNames {}));
123+
let errors = test_operation_with_schema(
124+
"query Foo($var: [JumbledUpLetters!]!) {
125+
user(id: 4) {
126+
name
127+
pets { ... on Badger { name }, ...PetFields }
128+
}
129+
}
130+
131+
fragment PetFields on Peat {
132+
name
133+
}",
134+
&TEST_SCHEMA,
135+
&mut plan,
136+
);
137+
138+
let messages = get_messages(&errors);
139+
assert_eq!(messages.len(), 3);
140+
assert_eq!(
141+
messages,
142+
vec![
143+
"Unknown type \"JumbledUpLetters\".",
144+
"Unknown type \"Badger\".",
145+
"Unknown type \"Peat\"."
146+
]
147+
);
148+
}

src/validation/rules/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ pub mod defaults;
22
pub mod fields_on_correct_type;
33
pub mod fragments_on_composite_types;
44
pub mod known_fragment_names;
5+
pub mod known_type_names;
56
pub mod leaf_field_selections;
67
pub mod lone_anonymous_operation;
78
pub mod no_unused_fragments;
@@ -14,6 +15,7 @@ pub use self::defaults::*;
1415
pub use self::fields_on_correct_type::*;
1516
pub use self::fragments_on_composite_types::*;
1617
pub use self::known_fragment_names::*;
18+
pub use self::known_type_names::*;
1719
pub use self::leaf_field_selections::*;
1820
pub use self::lone_anonymous_operation::*;
1921
pub use self::no_unused_fragments::*;

src/validation/test_utils.rs

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,105 @@ scalar Int
2020
scalar ID
2121
scalar String
2222
23+
type Query {
24+
__schema: __Schema!
25+
__type(name: String!): __Type
26+
}
27+
28+
type __Schema {
29+
types: [__Type!]!
30+
queryType: __Type!
31+
mutationType: __Type
32+
subscriptionType: __Type
33+
directives: [__Directive!]!
34+
}
35+
36+
type __Type {
37+
kind: __TypeKind!
38+
name: String
39+
description: String
40+
41+
# OBJECT and INTERFACE only
42+
fields(includeDeprecated: Boolean = false): [__Field!]
43+
44+
# OBJECT only
45+
interfaces: [__Type!]
46+
47+
# INTERFACE and UNION only
48+
possibleTypes: [__Type!]
49+
50+
# ENUM only
51+
enumValues(includeDeprecated: Boolean = false): [__EnumValue!]
52+
53+
# INPUT_OBJECT only
54+
inputFields: [__InputValue!]
55+
56+
# NON_NULL and LIST only
57+
ofType: __Type
58+
}
59+
60+
type __Field {
61+
name: String!
62+
description: String
63+
args: [__InputValue!]!
64+
type: __Type!
65+
isDeprecated: Boolean!
66+
deprecationReason: String
67+
}
68+
69+
type __InputValue {
70+
name: String!
71+
description: String
72+
type: __Type!
73+
defaultValue: String
74+
}
75+
76+
type __EnumValue {
77+
name: String!
78+
description: String
79+
isDeprecated: Boolean!
80+
deprecationReason: String
81+
}
82+
83+
enum __TypeKind {
84+
SCALAR
85+
OBJECT
86+
INTERFACE
87+
UNION
88+
ENUM
89+
INPUT_OBJECT
90+
LIST
91+
NON_NULL
92+
}
93+
94+
type __Directive {
95+
name: String!
96+
description: String
97+
locations: [__DirectiveLocation!]!
98+
args: [__InputValue!]!
99+
}
100+
101+
enum __DirectiveLocation {
102+
QUERY
103+
MUTATION
104+
SUBSCRIPTION
105+
FIELD
106+
FRAGMENT_DEFINITION
107+
FRAGMENT_SPREAD
108+
INLINE_FRAGMENT
109+
SCHEMA
110+
SCALAR
111+
OBJECT
112+
FIELD_DEFINITION
113+
ARGUMENT_DEFINITION
114+
INTERFACE
115+
UNION
116+
ENUM
117+
ENUM_VALUE
118+
INPUT_OBJECT
119+
INPUT_FIELD_DEFINITION
120+
}
121+
23122
interface Mammal {
24123
mother: Mammal
25124
father: Mammal

0 commit comments

Comments
 (0)