Skip to content

Commit 4433705

Browse files
committed
added rule VariablesAreInputTypes
1 parent f6ce7dd commit 4433705

File tree

4 files changed

+121
-3
lines changed

4 files changed

+121
-3
lines changed

src/ast/ext.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,8 +199,8 @@ impl TypeDefinitionExtension for schema::TypeDefinition {
199199
schema::TypeDefinition::Object(_o) => false,
200200
schema::TypeDefinition::Interface(_i) => false,
201201
schema::TypeDefinition::Union(_u) => false,
202-
schema::TypeDefinition::Scalar(_u) => false,
203-
schema::TypeDefinition::Enum(_u) => false,
202+
schema::TypeDefinition::Scalar(_u) => true,
203+
schema::TypeDefinition::Enum(_u) => true,
204204
schema::TypeDefinition::InputObject(_u) => true,
205205
}
206206
}

src/validation/rules/defaults.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::validation::validate::ValidationPlan;
33
use super::{
44
FieldsOnCorrectType, FragmentsOnCompositeTypes, KnownFragmentNamesRule, KnownTypeNames,
55
LeafFieldSelections, LoneAnonymousOperation, NoUnusedFragments, OverlappingFieldsCanBeMerged,
6-
SingleFieldSubscriptions, UniqueOperationNames,
6+
SingleFieldSubscriptions, UniqueOperationNames, VariablesAreInputTypes,
77
};
88

99
pub fn default_rules_validation_plan() -> ValidationPlan {
@@ -19,6 +19,7 @@ pub fn default_rules_validation_plan() -> ValidationPlan {
1919
plan.add_rule(Box::new(LeafFieldSelections {}));
2020
plan.add_rule(Box::new(UniqueOperationNames {}));
2121
plan.add_rule(Box::new(SingleFieldSubscriptions {}));
22+
plan.add_rule(Box::new(VariablesAreInputTypes {}));
2223

2324
plan
2425
}

src/validation/rules/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ pub mod overlapping_fields_can_be_merged;
1111
pub mod rule;
1212
pub mod single_field_subscriptions;
1313
pub mod unique_operation_names;
14+
pub mod variables_are_input_types;
1415

1516
pub use self::defaults::*;
1617
pub use self::fields_on_correct_type::*;
@@ -24,3 +25,4 @@ pub use self::overlapping_fields_can_be_merged::*;
2425
pub use self::rule::*;
2526
pub use self::single_field_subscriptions::*;
2627
pub use self::unique_operation_names::*;
28+
pub use self::variables_are_input_types::*;
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
use super::ValidationRule;
2+
use crate::ast::{AstTypeRef, TypeDefinitionExtension, TypeInfoQueryVisitor};
3+
use crate::validation::utils::ValidationContext;
4+
use crate::validation::utils::{ValidationError, ValidationErrorContext};
5+
6+
/// Variables are input types
7+
///
8+
/// A GraphQL operation is only valid if all the variables it defines are of
9+
/// input types (scalar, enum, or input object).
10+
///
11+
/// See https://spec.graphql.org/draft/#sec-Variables-Are-Input-Types
12+
pub struct VariablesAreInputTypes;
13+
14+
impl<'a> TypeInfoQueryVisitor<ValidationErrorContext<'a>> for VariablesAreInputTypes {
15+
fn enter_variable_definition(
16+
&self,
17+
_node: &crate::static_graphql::query::VariableDefinition,
18+
_parent_operation: &crate::static_graphql::query::OperationDefinition,
19+
_visitor_context: &mut ValidationErrorContext<'a>,
20+
_type_info: &crate::ast::TypeInfo,
21+
) {
22+
if let Some(schema_type) = _visitor_context
23+
.ctx
24+
.find_schema_definition_by_name(_node.var_type.named_type())
25+
{
26+
if !schema_type.is_input_type() {
27+
_visitor_context.report_error(ValidationError {
28+
message: format!(
29+
"Variable \"${}\" cannot be non-input type \"{}\".",
30+
_node.name, _node.var_type
31+
),
32+
locations: vec![_node.position],
33+
})
34+
}
35+
}
36+
}
37+
}
38+
39+
impl ValidationRule for VariablesAreInputTypes {
40+
fn validate<'a>(&self, ctx: &ValidationContext) -> Vec<ValidationError> {
41+
let mut error_context = ValidationErrorContext::new(ctx);
42+
43+
if let Some(type_info_registry) = &ctx.type_info_registry {
44+
self.visit_document(
45+
&ctx.operation.clone(),
46+
&mut error_context,
47+
&type_info_registry,
48+
);
49+
}
50+
51+
error_context.errors
52+
}
53+
}
54+
55+
#[test]
56+
fn unknown_types_are_ignored() {
57+
use crate::validation::test_utils::*;
58+
59+
let mut plan = create_plan_from_rule(Box::new(VariablesAreInputTypes {}));
60+
let errors = test_operation_with_schema(
61+
"
62+
query Foo($a: Unknown, $b: [[Unknown!]]!) {
63+
field(a: $a, b: $b)
64+
}",
65+
&TEST_SCHEMA,
66+
&mut plan,
67+
);
68+
69+
let messages = get_messages(&errors);
70+
assert_eq!(messages.len(), 0);
71+
}
72+
73+
#[test]
74+
fn input_types_are_valid() {
75+
use crate::validation::test_utils::*;
76+
77+
let mut plan = create_plan_from_rule(Box::new(VariablesAreInputTypes {}));
78+
let errors = test_operation_with_schema(
79+
"
80+
query Foo($a: String, $b: [Boolean!]!, $c: ComplexInput) {
81+
field(a: $a, b: $b, c: $c)
82+
}",
83+
&TEST_SCHEMA,
84+
&mut plan,
85+
);
86+
87+
let messages = get_messages(&errors);
88+
assert_eq!(messages.len(), 0);
89+
}
90+
91+
#[test]
92+
fn output_types_are_invalid() {
93+
use crate::validation::test_utils::*;
94+
95+
let mut plan = create_plan_from_rule(Box::new(VariablesAreInputTypes {}));
96+
let errors = test_operation_with_schema(
97+
"
98+
query Foo($a: Dog, $b: [[CatOrDog!]]!, $c: Pet) {
99+
field(a: $a, b: $b, c: $c)
100+
}",
101+
&TEST_SCHEMA,
102+
&mut plan,
103+
);
104+
105+
let messages = get_messages(&errors);
106+
assert_eq!(messages.len(), 3);
107+
assert_eq!(
108+
messages,
109+
vec![
110+
"Variable \"$a\" cannot be non-input type \"Dog\".",
111+
"Variable \"$b\" cannot be non-input type \"[[CatOrDog!]]!\".",
112+
"Variable \"$c\" cannot be non-input type \"Pet\".",
113+
]
114+
);
115+
}

0 commit comments

Comments
 (0)