@@ -114,45 +114,17 @@ def find_conflict(self, parent_fields_are_mutually_exclusive, response_name, fie
114114 [ast2 ]
115115 )
116116
117- subfield_map = self .get_subfield_map ( ast1 , type1 , ast2 , type2 )
117+ subfield_map = _get_subfield_map ( self .context , ast1 , type1 , ast2 , type2 )
118118 if subfield_map :
119119 conflicts = self .find_conflicts (fields_are_mutually_exclusive , subfield_map )
120- return self .subfield_conflicts (conflicts , response_name , ast1 , ast2 )
121-
122- def get_subfield_map (self , ast1 , type1 , ast2 , type2 ):
123- selection_set1 = ast1 .selection_set
124- selection_set2 = ast2 .selection_set
125-
126- if selection_set1 and selection_set2 :
127- visited_fragment_names = set ()
128-
129- subfield_map = self .collect_field_asts_and_defs (
130- get_named_type (type1 ),
131- selection_set1 ,
132- visited_fragment_names
133- )
134-
135- subfield_map = self .collect_field_asts_and_defs (
136- get_named_type (type2 ),
137- selection_set2 ,
138- visited_fragment_names ,
139- subfield_map
140- )
141- return subfield_map
142-
143- def subfield_conflicts (self , conflicts , response_name , ast1 , ast2 ):
144- if conflicts :
145- return (
146- (response_name , [conflict [0 ] for conflict in conflicts ]),
147- tuple (itertools .chain ([ast1 ], * [conflict [1 ] for conflict in conflicts ])),
148- tuple (itertools .chain ([ast2 ], * [conflict [2 ] for conflict in conflicts ]))
149- )
120+ return _subfield_conflicts (conflicts , response_name , ast1 , ast2 )
150121
151122 def leave_SelectionSet (self , node , key , parent , path , ancestors ):
152123 # Note: we validate on the reverse traversal so deeper conflicts will be
153124 # caught first, for correct calculation of mutual exclusivity and for
154125 # clearer error messages.
155- field_map = self .collect_field_asts_and_defs (
126+ field_map = _collect_field_asts_and_defs (
127+ self .context ,
156128 self .context .get_parent_type (),
157129 node
158130 )
@@ -199,62 +171,6 @@ def same_arguments(cls, arguments1, arguments2):
199171
200172 return True
201173
202- def collect_field_asts_and_defs (self , parent_type , selection_set , visited_fragment_names = None , ast_and_defs = None ):
203- if visited_fragment_names is None :
204- visited_fragment_names = set ()
205-
206- if ast_and_defs is None :
207- # An ordered dictionary is required, otherwise the error message will be out of order.
208- # We need to preserve the order that the item was inserted into the dict, as that will dictate
209- # in which order the reasons in the error message should show.
210- # Otherwise, the error messages will be inconsistently ordered for the same AST.
211- # And this can make it so that tests fail half the time, and fool a user into thinking that
212- # the errors are different, when in-fact they are the same, just that the ordering of the reasons differ.
213- ast_and_defs = DefaultOrderedDict (list )
214-
215- for selection in selection_set .selections :
216- if isinstance (selection , ast .Field ):
217- field_name = selection .name .value
218- field_def = None
219- if isinstance (parent_type , (GraphQLObjectType , GraphQLInterfaceType )):
220- field_def = parent_type .get_fields ().get (field_name )
221-
222- response_name = selection .alias .value if selection .alias else field_name
223- ast_and_defs [response_name ].append ((parent_type , selection , field_def ))
224-
225- elif isinstance (selection , ast .InlineFragment ):
226- type_condition = selection .type_condition
227- inline_fragment_type = \
228- type_from_ast (self .context .get_schema (), type_condition ) \
229- if type_condition else parent_type
230-
231- self .collect_field_asts_and_defs (
232- inline_fragment_type ,
233- selection .selection_set ,
234- visited_fragment_names ,
235- ast_and_defs
236- )
237-
238- elif isinstance (selection , ast .FragmentSpread ):
239- fragment_name = selection .name .value
240- if fragment_name in visited_fragment_names :
241- continue
242-
243- visited_fragment_names .add (fragment_name )
244- fragment = self .context .get_fragment (fragment_name )
245-
246- if not fragment :
247- continue
248-
249- self .collect_field_asts_and_defs (
250- type_from_ast (self .context .get_schema (), fragment .type_condition ),
251- fragment .selection_set ,
252- visited_fragment_names ,
253- ast_and_defs
254- )
255-
256- return ast_and_defs
257-
258174 @classmethod
259175 def fields_conflict_message (cls , reason_name , reason ):
260176 return (
@@ -272,6 +188,98 @@ def reason_message(cls, reason):
272188 return reason
273189
274190
191+ def _collect_field_asts_and_defs (context , parent_type , selection_set , visited_fragment_names = None , ast_and_defs = None ):
192+ if visited_fragment_names is None :
193+ visited_fragment_names = set ()
194+
195+ if ast_and_defs is None :
196+ # An ordered dictionary is required, otherwise the error message will be out of order.
197+ # We need to preserve the order that the item was inserted into the dict, as that will dictate
198+ # in which order the reasons in the error message should show.
199+ # Otherwise, the error messages will be inconsistently ordered for the same AST.
200+ # And this can make it so that tests fail half the time, and fool a user into thinking that
201+ # the errors are different, when in-fact they are the same, just that the ordering of the reasons differ.
202+ ast_and_defs = DefaultOrderedDict (list )
203+
204+ for selection in selection_set .selections :
205+ if isinstance (selection , ast .Field ):
206+ field_name = selection .name .value
207+ field_def = None
208+ if isinstance (parent_type , (GraphQLObjectType , GraphQLInterfaceType )):
209+ field_def = parent_type .get_fields ().get (field_name )
210+
211+ response_name = selection .alias .value if selection .alias else field_name
212+ ast_and_defs [response_name ].append ((parent_type , selection , field_def ))
213+
214+ elif isinstance (selection , ast .InlineFragment ):
215+ type_condition = selection .type_condition
216+ inline_fragment_type = \
217+ type_from_ast (context .get_schema (), type_condition ) \
218+ if type_condition else parent_type
219+
220+ _collect_field_asts_and_defs (
221+ context ,
222+ inline_fragment_type ,
223+ selection .selection_set ,
224+ visited_fragment_names ,
225+ ast_and_defs
226+ )
227+
228+ elif isinstance (selection , ast .FragmentSpread ):
229+ fragment_name = selection .name .value
230+ if fragment_name in visited_fragment_names :
231+ continue
232+
233+ visited_fragment_names .add (fragment_name )
234+ fragment = context .get_fragment (fragment_name )
235+
236+ if not fragment :
237+ continue
238+
239+ _collect_field_asts_and_defs (
240+ context ,
241+ type_from_ast (context .get_schema (), fragment .type_condition ),
242+ fragment .selection_set ,
243+ visited_fragment_names ,
244+ ast_and_defs
245+ )
246+
247+ return ast_and_defs
248+
249+
250+ def _get_subfield_map (context , ast1 , type1 , ast2 , type2 ):
251+ selection_set1 = ast1 .selection_set
252+ selection_set2 = ast2 .selection_set
253+
254+ if selection_set1 and selection_set2 :
255+ visited_fragment_names = set ()
256+
257+ subfield_map = _collect_field_asts_and_defs (
258+ context ,
259+ get_named_type (type1 ),
260+ selection_set1 ,
261+ visited_fragment_names
262+ )
263+
264+ subfield_map = _collect_field_asts_and_defs (
265+ context ,
266+ get_named_type (type2 ),
267+ selection_set2 ,
268+ visited_fragment_names ,
269+ subfield_map
270+ )
271+ return subfield_map
272+
273+
274+ def _subfield_conflicts (conflicts , response_name , ast1 , ast2 ):
275+ if conflicts :
276+ return (
277+ (response_name , [conflict [0 ] for conflict in conflicts ]),
278+ tuple (itertools .chain ([ast1 ], * [conflict [1 ] for conflict in conflicts ])),
279+ tuple (itertools .chain ([ast2 ], * [conflict [2 ] for conflict in conflicts ]))
280+ )
281+
282+
275283def do_types_conflict (type1 , type2 ):
276284 if isinstance (type1 , GraphQLList ):
277285 if isinstance (type2 , GraphQLList ):
0 commit comments