Skip to content

Commit 01325ef

Browse files
committed
Experimental: customizable validation
Closes #37
1 parent acfaae9 commit 01325ef

File tree

4 files changed

+78
-10
lines changed

4 files changed

+78
-10
lines changed

graphql/core/utils/type_info.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,17 @@ def pop(lst):
2222
@six.add_metaclass(visitor_meta.VisitorMeta)
2323
class TypeInfo(object):
2424
__slots__ = '_schema', '_type_stack', '_parent_type_stack', '_input_type_stack', '_field_def_stack', '_directive', \
25-
'_argument'
25+
'_argument', '_get_field_def_fn'
2626

27-
def __init__(self, schema):
27+
def __init__(self, schema, get_field_def_fn=get_field_def):
2828
self._schema = schema
2929
self._type_stack = []
3030
self._parent_type_stack = []
3131
self._input_type_stack = []
3232
self._field_def_stack = []
3333
self._directive = None
3434
self._argument = None
35+
self._get_field_def_fn = get_field_def_fn
3536

3637
def get_type(self):
3738
if self._type_stack:
@@ -76,7 +77,7 @@ def enter_Field(self, node):
7677
parent_type = self.get_parent_type()
7778
field_def = None
7879
if parent_type:
79-
field_def = get_field_def(self._schema, parent_type, node)
80+
field_def = self._get_field_def_fn(self._schema, parent_type, node)
8081
self._field_def_stack.append(field_def)
8182
self._type_stack.append(field_def and field_def.type)
8283

graphql/core/validation/__init__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ def validate(schema, ast, rules=specified_rules):
1010
assert schema, 'Must provide schema'
1111
assert ast, 'Must provide document'
1212
assert isinstance(schema, GraphQLSchema)
13-
return visit_using_rules(schema, ast, rules)
13+
type_info = TypeInfo(schema)
14+
return visit_using_rules(schema, type_info, ast, rules)
1415

1516

16-
def visit_using_rules(schema, ast, rules):
17-
type_info = TypeInfo(schema)
17+
def visit_using_rules(schema, type_info, ast, rules):
1818
context = ValidationContext(schema, ast, type_info)
1919
errors = []
2020
rules = [rule(context) for rule in rules]
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
from graphql.core import parse
2+
from graphql.core import validate
3+
from graphql.core.utils.type_info import TypeInfo
4+
from graphql.core.validation import visit_using_rules
5+
from graphql.core.validation.rules import specified_rules
6+
from utils import test_schema
7+
8+
9+
def expect_valid(schema, query_string):
10+
errors = validate(schema, parse(query_string))
11+
assert not errors
12+
13+
14+
def test_it_validates_queries():
15+
expect_valid(test_schema, '''
16+
query {
17+
catOrDog {
18+
... on Cat {
19+
furColor
20+
}
21+
... on Dog {
22+
isHousetrained
23+
}
24+
}
25+
}
26+
''')
27+
28+
29+
def test_validates_using_a_custom_type_info():
30+
type_info = TypeInfo(test_schema, lambda *_: None)
31+
32+
ast = parse('''
33+
query {
34+
catOrDog {
35+
... on Cat {
36+
furColor
37+
}
38+
... on Dog {
39+
isHousetrained
40+
}
41+
}
42+
}
43+
''')
44+
45+
errors = visit_using_rules(
46+
test_schema,
47+
type_info,
48+
ast,
49+
specified_rules
50+
)
51+
52+
assert len(errors) == 1
53+
assert errors[0].message == 'Cannot query field "catOrDog" on "QueryRoot".'

tests/core_validation/utils.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,24 @@
4444
'surname': GraphQLArgument(GraphQLBoolean),
4545
}),
4646
'nickname': GraphQLField(GraphQLString),
47+
'barkVolume': GraphQLField(GraphQLInt),
4748
'barks': GraphQLField(GraphQLBoolean),
4849
'doesKnowCommand': GraphQLField(GraphQLBoolean, {
4950
'dogCommand': GraphQLArgument(DogCommand)
50-
})
51+
}),
52+
'isHousetrained': GraphQLField(
53+
GraphQLBoolean,
54+
args={
55+
'atOtherHomes': GraphQLArgument(GraphQLBoolean, default_value=True)
56+
}
57+
),
58+
'isAtLocation': GraphQLField(
59+
GraphQLBoolean,
60+
args={
61+
'x': GraphQLArgument(GraphQLInt),
62+
'y': GraphQLArgument(GraphQLInt)
63+
}
64+
)
5165
}, interfaces=[Being, Pet], is_type_of=lambda: None)
5266

5367
Cat = GraphQLObjectType('Cat', lambda: {
@@ -163,7 +177,7 @@
163177
'complicatedArgs': GraphQLField(ComplicatedArgs),
164178
})
165179

166-
default_schema = GraphQLSchema(query=QueryRoot, directives=[
180+
test_schema = GraphQLSchema(query=QueryRoot, directives=[
167181
GraphQLDirective(name='operationOnly', on_operation=True),
168182
GraphQLIncludeDirective,
169183
GraphQLSkipDirective
@@ -202,11 +216,11 @@ def expect_invalid(schema, rules, query, expected_errors, sort_list=True):
202216

203217

204218
def expect_passes_rule(rule, query):
205-
return expect_valid(default_schema, [rule], query)
219+
return expect_valid(test_schema, [rule], query)
206220

207221

208222
def expect_fails_rule(rule, query, errors, sort_list=True):
209-
return expect_invalid(default_schema, [rule], query, errors, sort_list)
223+
return expect_invalid(test_schema, [rule], query, errors, sort_list)
210224

211225

212226
def expect_fails_rule_with_schema(schema, rule, query, errors, sort_list=True):

0 commit comments

Comments
 (0)