Skip to content

Commit 4833293

Browse files
author
rawls238
committed
implement possible fragment spreads + add DS_store to gitignore
1 parent 602d774 commit 4833293

File tree

3 files changed

+170
-4
lines changed

3 files changed

+170
-4
lines changed

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,4 +60,7 @@ docs/_build/
6060
target/
6161

6262
# IntelliJ
63-
.idea
63+
.idea
64+
65+
# Mac
66+
.DS_Store

graphql/core/validation/rules.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +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, GraphQLObjectType,
4-
GraphQLInterfaceType, GraphQLUnionType
3+
from ..type.definition import is_composite_type, is_input_type, is_leaf_type, GraphQLNonNull, GraphQLObjectType, GraphQLInterfaceType, GraphQLUnionType
54
from ..language import ast
65
from ..language.visitor import Visitor, visit
76
from ..language.printer import print_ast
@@ -246,7 +245,7 @@ def enter_InlineFragment(self, node, *args):
246245

247246
def enter_FragmentSpread(self, node, *args):
248247
frag_name = node.name.value
249-
frag_type = self.get_fragment_type(context, frag_name)
248+
frag_type = self.get_fragment_type(self.context, frag_name)
250249
parent_type = self.context.get_parent_type()
251250
if frag_type and parent_type and not self.do_types_overlap(frag_type, parent_type):
252251
return GraphQLError(
@@ -259,6 +258,20 @@ def get_fragment_type(context, name):
259258
frag = context.get_fragment(name)
260259
return frag and type_from_ast(context.get_schema(), frag.type_condition)
261260

261+
@staticmethod
262+
def do_types_overlap(t1, t2):
263+
if t1 == t2:
264+
return True
265+
if isinstance(t1, GraphQLObjectType):
266+
if isinstance(t2, GraphQLObjectType):
267+
return False
268+
return t2.get_possible_types().index(t1) != -1
269+
if isinstance(t1, GraphQLInterfaceType) or isinstance(t1, GraphQLUnionType):
270+
if isinstance(t2, GraphQLObjectType):
271+
return t1.get_possible_types().index(t2) != -1
272+
273+
t1_type_names = { possible_type['name']: possible_type for possible_type in t1.get_possible_types() }
274+
return any(t['name'] in t1_type_names for t in t2.get_possible_types())
262275

263276
@staticmethod
264277
def type_incompatible_spread_message(frag_name, parent_type, frag_type):
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
from graphql.core.language.location import SourceLocation
2+
from graphql.core.validation.rules import PossibleFragmentSpreads
3+
from utils import expect_passes_rule, expect_fails_rule
4+
5+
def error(frag_name, parent_type, frag_type, line, column):
6+
return {
7+
'message': PossibleFragmentSpreads.type_incompatible_spread_message(frag_name, parent_type, frag_type),
8+
'locations': [{ 'line': line, 'column': column}]
9+
}
10+
11+
12+
def error_annon(frag_name, parent_type, frag_type, line, column):
13+
return {
14+
'message': PossibleFragmentSpreads.type_incompatible_anon_spread_message(frag_name, parent_type, frag_type),
15+
'locations': [{ 'line': line, 'column': column}]
16+
}
17+
18+
def same_object():
19+
expect_passes_rule(PossibleFragmentSpreads, '''
20+
fragment objectWithinObject on Dog { ...dogFragment }
21+
fragment dogFragment on Dog { barkVolume }
22+
''')
23+
24+
def same_object_inline_frag():
25+
expect_passes_rule(PossibleFragmentSpreads, '''
26+
fragment objectWithinObjectAnon on Dog { ... on Dog { barkVolume } }
27+
''')
28+
29+
def object_into_implemented_interface():
30+
expect_passes_rule(PossibleFragmentSpreads, '''
31+
fragment objectWithinInterface on Pet { ...dogFragment }
32+
fragment dogFragment on Dog { barkVolume }
33+
''')
34+
35+
def object_into_containing_union():
36+
expect_passes_rule(PossibleFragmentSpreads, '''
37+
fragment objectWithinUnion on CatOrDog { ...dogFragment }
38+
fragment dogFragment on Dog { barkVolume }
39+
''')
40+
41+
def union_into_contained_object():
42+
expect_passes_rule(PossibleFragmentSpreads, '''
43+
fragment unionWithinObject on Dog { ...catOrDogFragment }
44+
fragment catOrDogFragment on CatOrDog { __typename }
45+
''')
46+
47+
def union_into_overlapping_interface():
48+
expect_passes_rule(PossibleFragmentSpreads, '''
49+
fragment unionWithinInterface on Pet { ...catOrDogFragment }
50+
fragment catOrDogFragment on CatOrDog { __typename }
51+
''')
52+
53+
def union_into_overlapping_union():
54+
expect_passes_rule(PossibleFragmentSpreads, '''
55+
fragment unionWithinUnion on DogOrHuman { ...catOrDogFragment }
56+
fragment catOrDogFragment on CatOrDog { __typename }
57+
''')
58+
59+
def interface_into_implemented_object():
60+
expect_passes_rule(PossibleFragmentSpreads, '''
61+
fragment interfaceWithinObject on Dog { ...petFragment }
62+
fragment petFragment on Pet { name }
63+
''')
64+
65+
def interface_into_overlapping_interface():
66+
expect_passes_rule(PossibleFragmentSpreads, '''
67+
fragment interfaceWithinInterface on Pet { ...beingFragment }
68+
fragment beingFragment on Being { name }
69+
''')
70+
71+
def interface_into_overlapping_interface_in_inline_fragment():
72+
expect_passes_rule(PossibleFragmentSpreads, '''
73+
fragment interfaceWithinInterface on Pet { ... on Being { name } }
74+
''')
75+
76+
def interface_into_overlapping_union():
77+
expect_passes_rule(PossibleFragmentSpreads, '''
78+
fragment interfaceWithinUnion on CatOrDog { ...petFragment }
79+
fragment petFragment on Pet { name }
80+
''')
81+
82+
def different_object_into_object():
83+
expect_fails_rule(PossibleFragmentSpreads, '''
84+
fragment invalidObjectWithinObject on Cat { ...dogFragment }
85+
fragment dogFragment on Dog { barkVolume }
86+
''', [error('dogFragment', 'Cat', 'Dog', 2, 51)])
87+
88+
def different_object_into_object_in_inline_fragment():
89+
expect_fails_rule(PossibleFragmentSpreads, '''
90+
fragment invalidObjectWithinObjectAnon on Cat {
91+
... on Dog { barkVolume }
92+
}
93+
''', [error_anon('Cat', 'Dog', 3, 9)])
94+
95+
def object_into_not_implementing_interface():
96+
expect_fails_rule(PossibleFragmentSpreads, '''
97+
fragment invalidObjectWithinInterface on Pet { ...humanFragment }
98+
fragment humanFragment on Human { pets { name } }
99+
''', [error('humanFragment', 'Pet', 'Human', 2, 54)])
100+
101+
def object_into_not_containing_union():
102+
expect_fails_rule(PossibleFragmentSpreads, '''
103+
fragment invalidObjectWithinUnion on CatOrDog { ...humanFragment }
104+
fragment humanFragment on Human { pets { name } }
105+
''', [error('humanFragment', 'CatOrDog', 'Human', 2, 55)])
106+
107+
def union_into_not_contained_object():
108+
expect_fails_rule(PossibleFragmentSpreads, '''
109+
fragment invalidUnionWithinObject on Human { ...catOrDogFragment }
110+
fragment catOrDogFragment on CatOrDog { __typename }
111+
''', [error('catOrDogFragment', 'Human', 'CatOrDog', 2, 52)])
112+
113+
def union_into_non_overlapping_interface():
114+
expect_fails_rule(PossibleFragmentSpreads, '''
115+
fragment invalidUnionWithinInterface on Pet { ...humanOrAlienFragment }
116+
fragment humanOrAlienFragment on HumanOrAlien { __typename }
117+
''', [error('humanOrAlienFragment', 'Pet', 'HumanOrAlien', 2, 53)])
118+
119+
def union_into_non_overlapping_union():
120+
expect_fails_rule(PossibleFragmentSpreads, '''
121+
fragment invalidUnionWithinUnion on CatOrDog { ...humanOrAlienFragment }
122+
fragment humanOrAlienFragment on HumanOrAlien { __typename }
123+
''', [error('humanOrAlienFragment', 'CatOrDog', 'HumanOrAlien', 2, 54)]);
124+
125+
def interface_into_non_implementing_object():
126+
expect_fails_rule(PossibleFragmentSpreads, '''
127+
fragment invalidInterfaceWithinObject on Cat { ...intelligentFragment }
128+
fragment intelligentFragment on Intelligent { iq }
129+
''', [error('intelligentFragment', 'Cat', 'Intelligent', 2, 54)])
130+
131+
def interface_into_non_overlapping_interface():
132+
expect_fails_rule(PossibleFragmentSpreads, '''
133+
fragment invalidInterfaceWithinInterface on Pet {
134+
...intelligentFragment
135+
}
136+
fragment intelligentFragment on Intelligent { iq }
137+
''', [error('intelligentFragment', 'Pet', 'Intelligent', 3, 9)]);
138+
139+
def interface_into_non_overlapping_interface_in_inline_fragment():
140+
expect_fails_rule(PossibleFragmentSpreads, '''
141+
fragment invalidInterfaceWithinInterfaceAnon on Pet {
142+
...on Intelligent { iq }
143+
}
144+
''', [error_anon('Pet', 'Intelligent', 3, 9)]);
145+
146+
def interface_into_non_overlapping_union():
147+
expect_fails_rule(PossibleFragmentSpreads, '''
148+
fragment invalidInterfaceWithinUnion on HumanOrAlien { ...petFragment }
149+
fragment petFragment on Pet { name }
150+
''', [error('petFragment', 'HumanOrAlien', 'Pet', 2, 62)]);

0 commit comments

Comments
 (0)