Skip to content

Commit f43006d

Browse files
committed
Merge pull request #38 from rawls238/guy_variables_in_allowed_position
Variables in allowed position
2 parents d893da4 + 7fd658e commit f43006d

File tree

3 files changed

+289
-2
lines changed

3 files changed

+289
-2
lines changed

graphql/core/.DS_Store

6 KB
Binary file not shown.

graphql/core/validation/rules.py

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from ..utils import type_from_ast, is_valid_literal_value
22
from ..error import GraphQLError
3-
from ..type.definition import is_composite_type, is_input_type, is_leaf_type, GraphQLNonNull
3+
from ..type.definition import is_composite_type, is_input_type, is_leaf_type, GraphQLNonNull, GraphQLList
44
from ..language import ast
55
from ..language.visitor import Visitor, visit
66
from ..language.printer import print_ast
@@ -623,7 +623,61 @@ def bad_value_for_default_arg_message(var_name, type, value):
623623

624624

625625
class VariablesInAllowedPosition(ValidationRule):
626-
pass
626+
visit_spread_fragments = True
627+
628+
def __init__(self, context):
629+
super(VariablesInAllowedPosition, self).__init__(context)
630+
self.var_def_map = {}
631+
self.visited_fragment_names = set()
632+
633+
def enter_OperationDefinition(self, *args):
634+
self.var_def_map = {}
635+
self.visited_fragment_names = set()
636+
637+
def enter_VariableDefinition(self, node, *args):
638+
self.var_def_map[node.variable.name.value] = node
639+
640+
def enter_Variable(self, node, *args):
641+
var_name = node.name.value
642+
var_def = self.var_def_map.get(var_name)
643+
var_type = var_def and type_from_ast(self.context.get_schema(), var_def.type)
644+
input_type = self.context.get_input_type()
645+
if var_type and input_type and not self.var_type_allowed_for_type(self.effective_type(var_type, var_def),
646+
input_type):
647+
return GraphQLError(self.bad_var_pos_message(var_name, var_type, input_type),
648+
[node])
649+
650+
def enter_FragmentSpread(self, node, *args):
651+
if node.name.value in self.visited_fragment_names:
652+
return False
653+
self.visited_fragment_names.add(node.name.value)
654+
655+
@staticmethod
656+
def effective_type(var_type, var_def):
657+
if not var_def.default_value or isinstance(var_def, GraphQLNonNull):
658+
return var_type
659+
660+
return GraphQLNonNull(var_type)
661+
662+
@staticmethod
663+
def var_type_allowed_for_type(var_type, expected_type):
664+
if isinstance(expected_type, GraphQLNonNull):
665+
if isinstance(var_type, GraphQLNonNull):
666+
return VariablesInAllowedPosition.var_type_allowed_for_type(var_type.of_type, expected_type.of_type)
667+
668+
return False
669+
670+
if isinstance(var_type, GraphQLNonNull):
671+
return VariablesInAllowedPosition.var_type_allowed_for_type(var_type.of_type, expected_type)
672+
673+
if isinstance(var_type, GraphQLList) and isinstance(expected_type, GraphQLList):
674+
return VariablesInAllowedPosition.var_type_allowed_for_type(var_type.of_type, expected_type.of_type)
675+
676+
return var_type == expected_type
677+
678+
@staticmethod
679+
def bad_var_pos_message(var_name, var_type, expected_type):
680+
return 'Variable "{}" of type "{}" used in position expecting type "{}".'.format(var_name, var_type, expected_type)
627681

628682

629683
class OverlappingFieldsCanBeMerged(ValidationRule):
Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
from graphql.core.language.location import SourceLocation
2+
from graphql.core.validation.rules import VariablesInAllowedPosition
3+
from utils import expect_passes_rule, expect_fails_rule
4+
5+
6+
def test_boolean_boolean():
7+
expect_passes_rule(VariablesInAllowedPosition, '''
8+
query Query($booleanArg: Boolean)
9+
{
10+
complicatedArgs {
11+
booleanArgField(booleanArg: $booleanArg)
12+
}
13+
}
14+
''')
15+
16+
17+
def test_boolean_boolean_in_fragment():
18+
expect_passes_rule(VariablesInAllowedPosition, '''
19+
fragment booleanArgFrag on ComplicatedArgs {
20+
booleanArgField(booleanArg: $booleanArg)
21+
}
22+
23+
query Query($booleanArg: Boolean)
24+
{
25+
complicatedArgs {
26+
...booleanArgFrag
27+
}
28+
}
29+
''')
30+
31+
expect_passes_rule(VariablesInAllowedPosition, '''
32+
query Query($booleanArg: Boolean)
33+
{
34+
complicatedArgs {
35+
...booleanArgFrag
36+
}
37+
}
38+
fragment booleanArgFrag on ComplicatedArgs {
39+
booleanArgField(booleanArg: $booleanArg)
40+
}
41+
''')
42+
43+
def test_non_null_boolean_boolean():
44+
expect_passes_rule(VariablesInAllowedPosition, '''
45+
query Query($nonNullBooleanArg: Boolean!)
46+
{
47+
complicatedArgs {
48+
booleanArgField(booleanArg: $nonNullBooleanArg)
49+
}
50+
}
51+
''')
52+
53+
def test_int_non_nll_int_with_default():
54+
expect_passes_rule(VariablesInAllowedPosition, '''
55+
query Query($intArg: Int = 1)
56+
{
57+
complicatedArgs {
58+
nonNullIntArgField(nonNullIntArg: $intArg)
59+
}
60+
}
61+
''')
62+
63+
def test_string_string():
64+
expect_passes_rule(VariablesInAllowedPosition, '''
65+
query Query($stringListVar: [String])
66+
{
67+
complicatedArgs {
68+
stringListArgField(stringListArg: $stringListVar)
69+
}
70+
}
71+
''')
72+
73+
def test_non_null_string_string():
74+
expect_passes_rule(VariablesInAllowedPosition, '''
75+
query Query($stringListVar: [String!])
76+
{
77+
complicatedArgs {
78+
stringListArgField(stringListArg: $stringListVar)
79+
}
80+
}
81+
''')
82+
83+
def test_string_string_item_position():
84+
expect_passes_rule(VariablesInAllowedPosition, '''
85+
query Query($stringVar: String)
86+
{
87+
complicatedArgs {
88+
stringListArgField(stringListArg: [$stringVar])
89+
}
90+
}
91+
''')
92+
93+
def test_non_null_string_string_item_positiion():
94+
expect_passes_rule(VariablesInAllowedPosition, '''
95+
query Query($stringVar: String!)
96+
{
97+
complicatedArgs {
98+
stringListArgField(stringListArg: [$stringVar])
99+
}
100+
}
101+
''')
102+
103+
def test_complex_input_complex_input():
104+
expect_passes_rule(VariablesInAllowedPosition, '''
105+
query Query($complexVar: ComplexInput)
106+
{
107+
complicatedArgs {
108+
complexArgField(complexArg: $ComplexInput)
109+
}
110+
}
111+
''')
112+
113+
def test_complex_input_complex_input_in_field_position():
114+
expect_passes_rule(VariablesInAllowedPosition, '''
115+
query Query($boolVar: Boolean = false)
116+
{
117+
complicatedArgs {
118+
complexArgField(complexArg: {requiredArg: $boolVar})
119+
}
120+
}
121+
''')
122+
123+
def test_boolean_non_null_boolean_in_directive():
124+
expect_passes_rule(VariablesInAllowedPosition, '''
125+
query Query($boolVar: Boolean!)
126+
{
127+
dog @include(if: $boolVar)
128+
}
129+
''')
130+
131+
def test_boolean_non_null_boolean_in_directive_with_default():
132+
expect_passes_rule(VariablesInAllowedPosition, '''
133+
query Query($boolVar: Boolean = false)
134+
{
135+
dog @include(if: $boolVar)
136+
}
137+
''')
138+
139+
def test_int_non_null_int():
140+
expect_fails_rule(VariablesInAllowedPosition, '''
141+
query Query($intArg: Int)
142+
{
143+
complicatedArgs {
144+
nonNullIntArgField(nonNullIntArg: $intArg)
145+
}
146+
}
147+
''', [
148+
{ 'message': VariablesInAllowedPosition.bad_var_pos_message('intArg', 'Int', 'Int!'),
149+
'locations': [SourceLocation(5, 45)] }
150+
])
151+
152+
def test_int_non_null_int_within_fragment():
153+
expect_fails_rule(VariablesInAllowedPosition, '''
154+
fragment nonNullIntArgFieldFrag on ComplicatedArgs {
155+
nonNullIntArgField(nonNullIntArg: $intArg)
156+
}
157+
query Query($intArg: Int)
158+
{
159+
complicatedArgs {
160+
...nonNullIntArgFieldFrag
161+
}
162+
}
163+
''', [
164+
{ 'message': VariablesInAllowedPosition.bad_var_pos_message('intArg', 'Int', 'Int!'),
165+
'locations': [SourceLocation(3, 43)] }
166+
])
167+
168+
def test_int_non_null_int_within_nested_fragment():
169+
expect_fails_rule(VariablesInAllowedPosition, '''
170+
fragment outerFrag on ComplicatedArgs {
171+
...nonNullIntArgFieldFrag
172+
}
173+
fragment nonNullIntArgFieldFrag on ComplicatedArgs {
174+
nonNullIntArgField(nonNullIntArg: $intArg)
175+
}
176+
query Query($intArg: Int)
177+
{
178+
complicatedArgs {
179+
...outerFrag
180+
}
181+
}
182+
''', [
183+
{ 'message': VariablesInAllowedPosition.bad_var_pos_message('intArg', 'Int', 'Int!'),
184+
'locations': [SourceLocation(6, 43)] }
185+
])
186+
187+
def test_string_over_boolean():
188+
expect_fails_rule(VariablesInAllowedPosition, '''
189+
query Query($stringVar: String)
190+
{
191+
complicatedArgs {
192+
booleanArgField(booleanArg: $stringVar)
193+
}
194+
}
195+
''', [
196+
{ 'message': VariablesInAllowedPosition.bad_var_pos_message('stringVar', 'String', 'Boolean'),
197+
'locations': [SourceLocation(5, 39)] }
198+
])
199+
200+
def test_string_string_fail():
201+
expect_fails_rule(VariablesInAllowedPosition, '''
202+
query Query($stringVar: String)
203+
{
204+
complicatedArgs {
205+
stringListArgField(stringListArg: $stringVar)
206+
}
207+
}
208+
''', [
209+
{ 'message': VariablesInAllowedPosition.bad_var_pos_message('stringVar', 'String', '[String]'),
210+
'locations': [SourceLocation(5, 45)]}
211+
])
212+
213+
def test_boolean_non_null_boolean_in_directive():
214+
expect_fails_rule(VariablesInAllowedPosition, '''
215+
query Query($boolVar: Boolean)
216+
{
217+
dog @include(if: $boolVar)
218+
}
219+
''', [
220+
{ 'message': VariablesInAllowedPosition.bad_var_pos_message('boolVar', 'Boolean', 'Boolean!'),
221+
'locations': [SourceLocation(4, 26)]
222+
}])
223+
224+
def test_string_non_null_boolean_in_directive():
225+
expect_fails_rule(VariablesInAllowedPosition, '''
226+
query Query($stringVar: String)
227+
{
228+
dog @include(if: $stringVar)
229+
}
230+
''', [
231+
{ 'message': VariablesInAllowedPosition.bad_var_pos_message('stringVar', 'String', 'Boolean!'),
232+
'locations': [SourceLocation(4, 26)] }
233+
])

0 commit comments

Comments
 (0)