Skip to content

Commit 9d302f5

Browse files
committed
Factor out instance methods to normal methods. Commit: graphql/graphql-js@3a01fac
1 parent b0f9af7 commit 9d302f5

File tree

1 file changed

+96
-88
lines changed

1 file changed

+96
-88
lines changed

graphql/validation/rules/overlapping_fields_can_be_merged.py

Lines changed: 96 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
275283
def do_types_conflict(type1, type2):
276284
if isinstance(type1, GraphQLList):
277285
if isinstance(type2, GraphQLList):

0 commit comments

Comments
 (0)