Skip to content

Commit 1cdd8d1

Browse files
Marc-Andre Girouxrmosolgo
authored andcommitted
track all parents
1 parent 980f90e commit 1cdd8d1

File tree

2 files changed

+43
-30
lines changed

2 files changed

+43
-30
lines changed

lib/graphql/static_validation/rules/fields_will_merge.rb

Lines changed: 42 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# frozen_string_literal: true
1+
# frozen_string_literal: true
22
module GraphQL
33
module StaticValidation
44
module FieldsWillMerge
@@ -8,8 +8,8 @@ module FieldsWillMerge
88
#
99
# Original Algorithm: https://github.com/graphql/graphql-js/blob/master/src/validation/rules/OverlappingFieldsCanBeMerged.js
1010
NO_ARGS = {}.freeze
11-
Field = Struct.new(:node, :definition, :parent_type)
12-
FragmentSpread = Struct.new(:name, :parent_type)
11+
Field = Struct.new(:node, :definition, :parents)
12+
FragmentSpread = Struct.new(:name, :parents)
1313

1414
def initialize(*)
1515
super
@@ -33,14 +33,14 @@ def on_field(node, _parent)
3333
def conflicts_within_selection_set(node, parent_type)
3434
return if parent_type.nil?
3535

36-
fields, fragment_spreads = fields_and_fragments_from_selection(node, parent_type: parent_type)
36+
fields, fragment_spreads = fields_and_fragments_from_selection(node, parents: [parent_type])
3737

3838
# (A) Find find all conflicts "within" the fields of this selection set.
3939
find_conflicts_within(fields)
4040

4141
fragment_spreads.each_with_index do |fragment_spread, i|
4242
are_mutually_exclusive = mutually_exclusive?(
43-
fragment_spread.parent_type,
43+
fragment_spread.parents.last,
4444
parent_type
4545
)
4646

@@ -58,8 +58,8 @@ def conflicts_within_selection_set(node, parent_type)
5858
# item in that same list (except for itself).
5959
fragment_spreads[i + 1..-1].each do |fragment_spread2|
6060
are_mutually_exclusive = mutually_exclusive?(
61-
fragment_spread.parent_type,
62-
fragment_spread2.parent_type
61+
fragment_spread.parents.last,
62+
fragment_spread2.parents.last
6363
)
6464

6565
find_conflicts_between_fragments(
@@ -97,8 +97,14 @@ def find_conflicts_between_fragments(fragment_spread1, fragment_spread2, mutuall
9797

9898
return if fragment_type1.nil? || fragment_type2.nil?
9999

100-
fragment_fields1, fragment_spreads1 = fields_and_fragments_from_selection(fragment1, parent_type: fragment_type1)
101-
fragment_fields2, fragment_spreads2 = fields_and_fragments_from_selection(fragment2, parent_type: fragment_type2)
100+
fragment_fields1, fragment_spreads1 = fields_and_fragments_from_selection(
101+
fragment1,
102+
parents: [*fragment_spread1.parents, fragment_type1]
103+
)
104+
fragment_fields2, fragment_spreads2 = fields_and_fragments_from_selection(
105+
fragment2,
106+
parents: [*fragment_spread2, fragment_type2]
107+
)
102108

103109
# (F) First, find all conflicts between these two collections of fields
104110
# (not including any nested fragments).
@@ -140,7 +146,7 @@ def find_conflicts_between_fields_and_fragment(fragment_spread, fields, mutually
140146
fragment_type = context.schema.types[fragment.type.name]
141147
return if fragment_type.nil?
142148

143-
fragment_fields, fragment_spreads = fields_and_fragments_from_selection(fragment, parent_type: fragment_type)
149+
fragment_fields, fragment_spreads = fields_and_fragments_from_selection(fragment, parents: [*fragment_spread.parents, fragment_type])
144150

145151
# (D) First find any conflicts between the provided collection of fields
146152
# and the collection of fields represented by the given fragment.
@@ -174,8 +180,8 @@ def find_conflicts_within(response_keys)
174180
end
175181

176182
def find_conflict(response_key, field1, field2, mutually_exclusive: false)
177-
parent_type1 = field1.parent_type
178-
parent_type2 = field2.parent_type
183+
parent_type1 = field1.parents.last
184+
parent_type2 = field2.parents.last
179185

180186
node1 = field1.node
181187
node2 = field2.node
@@ -207,11 +213,17 @@ def find_conflict(response_key, field1, field2, mutually_exclusive: false)
207213
def find_conflicts_between_sub_selection_sets(field1, field2, mutually_exclusive:)
208214
return if field1.definition.nil? || field2.definition.nil?
209215

210-
parent_type1 = field1.definition.type.unwrap
211-
parent_type2 = field2.definition.type.unwrap
216+
return_type1 = field1.definition.type.unwrap
217+
return_type2 = field2.definition.type.unwrap
212218

213-
fields, fragment_spreads = fields_and_fragments_from_selection(field1.node, parent_type: parent_type1)
214-
fields2, fragment_spreads2 = fields_and_fragments_from_selection(field2.node, parent_type: parent_type2)
219+
fields, fragment_spreads = fields_and_fragments_from_selection(
220+
field1.node,
221+
parents: [*field1.parents, return_type1])
222+
223+
fields2, fragment_spreads2 = fields_and_fragments_from_selection(
224+
field2.node,
225+
parents: [*field2.parents, return_type2]
226+
)
215227

216228
# (H) First, collect all conflicts between these two collections of field.
217229
find_conflicts_between(fields, fields2, mutually_exclusive: mutually_exclusive)
@@ -220,8 +232,8 @@ def find_conflicts_between_sub_selection_sets(field1, field2, mutually_exclusive
220232
# those referenced by each fragment name associated with the second.
221233
fragment_spreads2.each do |fragment_spread|
222234
fragment_is_mutually_exclusive = mutually_exclusive?(
223-
parent_type1,
224-
fragment_spread.parent_type
235+
return_type1,
236+
fragment_spread.parents.last
225237
)
226238

227239
find_conflicts_between_fields_and_fragment(
@@ -235,8 +247,8 @@ def find_conflicts_between_sub_selection_sets(field1, field2, mutually_exclusive
235247
# those referenced by each fragment name associated with the first.
236248
fragment_spreads.each do |fragment_spread|
237249
fragment_is_mutually_exclusive = mutually_exclusive?(
238-
parent_type2,
239-
fragment_spread.parent_type
250+
return_type2,
251+
fragment_spread.parents.last
240252
)
241253

242254
find_conflicts_between_fields_and_fragment(
@@ -252,8 +264,8 @@ def find_conflicts_between_sub_selection_sets(field1, field2, mutually_exclusive
252264
fragment_spreads.each do |frag1|
253265
fragment_spreads2.each do |frag2|
254266
fragments_are_mutually_exclusive = mutually_exclusive?(
255-
frag1.parent_type,
256-
frag2.parent_type
267+
frag1.parents.last,
268+
frag2.parents.last
257269
)
258270

259271
find_conflicts_between_fragments(
@@ -283,25 +295,25 @@ def find_conflicts_between(response_keys, response_keys2, mutually_exclusive:)
283295
end
284296
end
285297

286-
def fields_and_fragments_from_selection(node, parent_type:)
298+
def fields_and_fragments_from_selection(node, parents:)
287299
@fields_and_fragments_from_node[node] ||= begin
288-
fields, fragment_spreads = find_fields_and_fragments(node.selections, parent_type: parent_type)
300+
fields, fragment_spreads = find_fields_and_fragments(node.selections, parents: parents)
289301
response_keys = fields.group_by { |f| f.node.alias || f.node.name }
290302
[response_keys, fragment_spreads]
291303
end
292304
end
293305

294-
def find_fields_and_fragments(selections, parent_type:, fields: [], fragment_spreads: [])
306+
def find_fields_and_fragments(selections, parents:, fields: [], fragment_spreads: [])
295307
selections.each do |node|
296308
case node
297309
when GraphQL::Language::Nodes::Field
298-
definition = context.schema.get_field(parent_type, node.name)
299-
fields << Field.new(node, definition, parent_type)
310+
definition = context.schema.get_field(parents.last, node.name)
311+
fields << Field.new(node, definition, parents)
300312
when GraphQL::Language::Nodes::InlineFragment
301-
fragment_type = node.type ? context.schema.types[node.type.name] : parent_type
302-
find_fields_and_fragments(node.selections, parent_type: fragment_type, fields: fields, fragment_spreads: fragment_spreads) if fragment_type
313+
fragment_type = node.type ? context.schema.types[node.type.name] : parents.last
314+
find_fields_and_fragments(node.selections, parents: [*parents, fragment_type], fields: fields, fragment_spreads: fragment_spreads) if fragment_type
303315
when GraphQL::Language::Nodes::FragmentSpread
304-
fragment_spreads << FragmentSpread.new(node.name, parent_type)
316+
fragment_spreads << FragmentSpread.new(node.name, parents)
305317
end
306318
end
307319

spec/graphql/static_validation/rules/fields_will_merge_spec.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,7 @@
527527
}
528528
|}
529529

530+
focus
530531
it "passes rule" do
531532
assert_equal [], errors
532533
end

0 commit comments

Comments
 (0)