@@ -114,45 +114,17 @@ def find_conflict(self, parent_fields_are_mutually_exclusive, response_name, fie
114
114
[ast2 ]
115
115
)
116
116
117
- subfield_map = self .get_subfield_map ( ast1 , type1 , ast2 , type2 )
117
+ subfield_map = _get_subfield_map ( self .context , ast1 , type1 , ast2 , type2 )
118
118
if subfield_map :
119
119
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 )
150
121
151
122
def leave_SelectionSet (self , node , key , parent , path , ancestors ):
152
123
# Note: we validate on the reverse traversal so deeper conflicts will be
153
124
# caught first, for correct calculation of mutual exclusivity and for
154
125
# clearer error messages.
155
- field_map = self .collect_field_asts_and_defs (
126
+ field_map = _collect_field_asts_and_defs (
127
+ self .context ,
156
128
self .context .get_parent_type (),
157
129
node
158
130
)
@@ -199,62 +171,6 @@ def same_arguments(cls, arguments1, arguments2):
199
171
200
172
return True
201
173
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
-
258
174
@classmethod
259
175
def fields_conflict_message (cls , reason_name , reason ):
260
176
return (
@@ -272,6 +188,98 @@ def reason_message(cls, reason):
272
188
return reason
273
189
274
190
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
+
275
283
def do_types_conflict (type1 , type2 ):
276
284
if isinstance (type1 , GraphQLList ):
277
285
if isinstance (type2 , GraphQLList ):
0 commit comments