graphql 1.12.25 → 1.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of graphql might be problematic. Click here for more details.

Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/mutation_generator.rb +1 -1
  3. data/lib/generators/graphql/type_generator.rb +0 -1
  4. data/lib/graphql/analysis/ast/field_usage.rb +4 -8
  5. data/lib/graphql/analysis/ast/query_complexity.rb +10 -14
  6. data/lib/graphql/analysis/ast/visitor.rb +4 -4
  7. data/lib/graphql/backtrace/table.rb +1 -1
  8. data/lib/graphql/dataloader.rb +55 -22
  9. data/lib/graphql/directive.rb +0 -4
  10. data/lib/graphql/enum_type.rb +5 -1
  11. data/lib/graphql/execution/errors.rb +1 -0
  12. data/lib/graphql/execution/interpreter/arguments.rb +1 -1
  13. data/lib/graphql/execution/interpreter/arguments_cache.rb +2 -2
  14. data/lib/graphql/execution/interpreter/runtime.rb +20 -12
  15. data/lib/graphql/execution/lookahead.rb +2 -2
  16. data/lib/graphql/execution/multiplex.rb +1 -1
  17. data/lib/graphql/introspection/directive_type.rb +1 -1
  18. data/lib/graphql/introspection/entry_points.rb +2 -2
  19. data/lib/graphql/introspection/enum_value_type.rb +2 -2
  20. data/lib/graphql/introspection/field_type.rb +2 -2
  21. data/lib/graphql/introspection/input_value_type.rb +4 -4
  22. data/lib/graphql/introspection/schema_type.rb +2 -2
  23. data/lib/graphql/introspection/type_type.rb +10 -10
  24. data/lib/graphql/language/block_string.rb +0 -4
  25. data/lib/graphql/language/document_from_schema_definition.rb +4 -2
  26. data/lib/graphql/language/lexer.rb +0 -3
  27. data/lib/graphql/language/lexer.rl +0 -4
  28. data/lib/graphql/language/nodes.rb +3 -14
  29. data/lib/graphql/language/parser.rb +442 -434
  30. data/lib/graphql/language/parser.y +5 -4
  31. data/lib/graphql/language/printer.rb +6 -1
  32. data/lib/graphql/language/sanitized_printer.rb +5 -5
  33. data/lib/graphql/language/token.rb +0 -4
  34. data/lib/graphql/name_validator.rb +0 -4
  35. data/lib/graphql/query/arguments.rb +1 -1
  36. data/lib/graphql/query/arguments_cache.rb +1 -1
  37. data/lib/graphql/query/context.rb +5 -2
  38. data/lib/graphql/query/literal_input.rb +1 -1
  39. data/lib/graphql/query/null_context.rb +12 -7
  40. data/lib/graphql/query/serial_execution/field_resolution.rb +1 -1
  41. data/lib/graphql/query/variables.rb +5 -1
  42. data/lib/graphql/relay/edges_instrumentation.rb +0 -1
  43. data/lib/graphql/rubocop/graphql/base_cop.rb +36 -0
  44. data/lib/graphql/rubocop/graphql/default_null_true.rb +43 -0
  45. data/lib/graphql/rubocop/graphql/default_required_true.rb +43 -0
  46. data/lib/graphql/rubocop.rb +4 -0
  47. data/lib/graphql/schema/addition.rb +37 -28
  48. data/lib/graphql/schema/argument.rb +11 -9
  49. data/lib/graphql/schema/build_from_definition.rb +12 -13
  50. data/lib/graphql/schema/directive/feature.rb +1 -1
  51. data/lib/graphql/schema/directive/flagged.rb +2 -2
  52. data/lib/graphql/schema/directive/include.rb +1 -1
  53. data/lib/graphql/schema/directive/skip.rb +1 -1
  54. data/lib/graphql/schema/directive/transform.rb +1 -1
  55. data/lib/graphql/schema/directive.rb +2 -6
  56. data/lib/graphql/schema/enum.rb +57 -9
  57. data/lib/graphql/schema/enum_value.rb +5 -1
  58. data/lib/graphql/schema/field/connection_extension.rb +1 -1
  59. data/lib/graphql/schema/field.rb +92 -18
  60. data/lib/graphql/schema/find_inherited_value.rb +1 -0
  61. data/lib/graphql/schema/finder.rb +5 -5
  62. data/lib/graphql/schema/input_object.rb +11 -13
  63. data/lib/graphql/schema/interface.rb +8 -19
  64. data/lib/graphql/schema/member/accepts_definition.rb +8 -1
  65. data/lib/graphql/schema/member/build_type.rb +0 -4
  66. data/lib/graphql/schema/member/has_arguments.rb +55 -13
  67. data/lib/graphql/schema/member/has_deprecation_reason.rb +1 -1
  68. data/lib/graphql/schema/member/has_fields.rb +76 -18
  69. data/lib/graphql/schema/member/has_interfaces.rb +90 -0
  70. data/lib/graphql/schema/member.rb +1 -0
  71. data/lib/graphql/schema/object.rb +7 -74
  72. data/lib/graphql/schema/printer.rb +1 -1
  73. data/lib/graphql/schema/relay_classic_mutation.rb +29 -3
  74. data/lib/graphql/schema/resolver/has_payload_type.rb +27 -2
  75. data/lib/graphql/schema/resolver.rb +19 -5
  76. data/lib/graphql/schema/subscription.rb +11 -1
  77. data/lib/graphql/schema/type_expression.rb +1 -1
  78. data/lib/graphql/schema/type_membership.rb +18 -4
  79. data/lib/graphql/schema/union.rb +6 -1
  80. data/lib/graphql/schema/validator/format_validator.rb +0 -4
  81. data/lib/graphql/schema/validator/numericality_validator.rb +1 -0
  82. data/lib/graphql/schema/warden.rb +116 -52
  83. data/lib/graphql/schema.rb +87 -15
  84. data/lib/graphql/static_validation/base_visitor.rb +5 -5
  85. data/lib/graphql/static_validation/definition_dependencies.rb +0 -1
  86. data/lib/graphql/static_validation/literal_validator.rb +1 -1
  87. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
  88. data/lib/graphql/static_validation/rules/fields_will_merge.rb +8 -15
  89. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +1 -3
  90. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +4 -4
  91. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +7 -7
  92. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +6 -4
  93. data/lib/graphql/subscriptions/event.rb +20 -12
  94. data/lib/graphql/subscriptions.rb +17 -19
  95. data/lib/graphql/types/relay/has_node_field.rb +1 -1
  96. data/lib/graphql/types/relay/has_nodes_field.rb +1 -1
  97. data/lib/graphql/version.rb +1 -1
  98. data/lib/graphql.rb +9 -31
  99. metadata +14 -6
@@ -4,8 +4,6 @@ module GraphQL
4
4
  class Schema
5
5
  # This class joins an object type to an abstract type (interface or union) of which
6
6
  # it is a member.
7
- #
8
- # TODO: Not yet implemented for interfaces.
9
7
  class TypeMembership
10
8
  # @return [Class<GraphQL::Schema::Object>]
11
9
  attr_accessor :object_type
@@ -26,9 +24,25 @@ module GraphQL
26
24
  end
27
25
 
28
26
  # @return [Boolean] if false, {#object_type} will be treated as _not_ a member of {#abstract_type}
29
- def visible?(_ctx)
30
- true
27
+ def visible?(ctx)
28
+ warden = Warden.from_context(ctx)
29
+ (@object_type.respond_to?(:visible?) ? warden.visible_type?(@object_type, ctx) : true) &&
30
+ (@abstract_type.respond_to?(:visible?) ? warden.visible_type?(@abstract_type, ctx) : true)
31
31
  end
32
+
33
+ def graphql_name
34
+ "#{@object_type.graphql_name}.#{@abstract_type.kind.interface? ? "implements" : "belongsTo" }.#{@abstract_type.graphql_name}"
35
+ end
36
+
37
+ def path
38
+ graphql_name
39
+ end
40
+
41
+ def inspect
42
+ "#<#{self.class} #{@object_type.inspect} => #{@abstract_type.inspect}>"
43
+ end
44
+
45
+ alias :type_class :itself
32
46
  end
33
47
  end
34
48
  end
@@ -19,8 +19,9 @@ module GraphQL
19
19
  end
20
20
  else
21
21
  visible_types = []
22
+ warden = Warden.from_context(context)
22
23
  type_memberships.each do |type_membership|
23
- if type_membership.visible?(context)
24
+ if warden.visible_type_membership?(type_membership, context)
24
25
  visible_types << type_membership.object_type
25
26
  end
26
27
  end
@@ -28,6 +29,10 @@ module GraphQL
28
29
  end
29
30
  end
30
31
 
32
+ def all_possible_types
33
+ type_memberships.map(&:object_type)
34
+ end
35
+
31
36
  def to_graphql
32
37
  type_defn = GraphQL::UnionType.new
33
38
  type_defn.name = graphql_name
@@ -18,10 +18,6 @@ module GraphQL
18
18
  # # It's pretty hard to come up with a legitimate use case for `without:`
19
19
  #
20
20
  class FormatValidator < Validator
21
- if !String.method_defined?(:match?)
22
- using GraphQL::StringMatchBackport
23
- end
24
-
25
21
  # @param with [RegExp, nil]
26
22
  # @param without [Regexp, nil]
27
23
  # @param message [String]
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module GraphQL
2
3
  class Schema
3
4
  class Validator
@@ -37,6 +37,50 @@ module GraphQL
37
37
  #
38
38
  # @api private
39
39
  class Warden
40
+ def self.from_context(context)
41
+ (context.respond_to?(:warden) && context.warden) || PassThruWarden
42
+ end
43
+
44
+ # @param visibility_method [Symbol] a Warden method to call for this entry
45
+ # @param entry [Object, Array<Object>] One or more definitions for a given name in a GraphQL Schema
46
+ # @param context [GraphQL::Query::Context]
47
+ # @param warden [Warden]
48
+ # @return [Object] `entry` or one of `entry`'s items if exactly one of them is visible for this context
49
+ # @return [nil] If neither `entry` nor any of `entry`'s items are visible for this context
50
+ def self.visible_entry?(visibility_method, entry, context, warden = Warden.from_context(context))
51
+ if entry.is_a?(Array)
52
+ visible_item = nil
53
+ entry.each do |item|
54
+ if warden.public_send(visibility_method, item, context)
55
+ if visible_item.nil?
56
+ visible_item = item
57
+ else
58
+ raise Schema::DuplicateNamesError, "Found two visible definitions for `#{item.path}`: #{visible_item.inspect}, #{item.inspect}"
59
+ end
60
+ end
61
+ end
62
+ visible_item
63
+ elsif warden.public_send(visibility_method, entry, context)
64
+ entry
65
+ else
66
+ nil
67
+ end
68
+ end
69
+
70
+ # This is used when a caller provides a Hash for context.
71
+ # We want to call the schema's hooks, but we don't have a full-blown warden.
72
+ # The `context` arguments to these methods exist purely to simplify the code that
73
+ # calls methods on this object, so it will have everything it needs.
74
+ class PassThruWarden
75
+ class << self
76
+ def visible_field?(field, ctx); field.visible?(ctx); end
77
+ def visible_argument?(arg, ctx); arg.visible?(ctx); end
78
+ def visible_type?(type, ctx); type.visible?(ctx); end
79
+ def visible_enum_value?(ev, ctx); ev.visible?(ctx); end
80
+ def visible_type_membership?(tm, ctx); tm.visible?(ctx); end
81
+ end
82
+ end
83
+
40
84
  # @param filter [<#call(member)>] Objects are hidden when `.call(member, ctx)` returns true
41
85
  # @param context [GraphQL::Query::Context]
42
86
  # @param schema [GraphQL::Schema]
@@ -54,8 +98,8 @@ module GraphQL
54
98
  def types
55
99
  @types ||= begin
56
100
  vis_types = {}
57
- @schema.types.each do |n, t|
58
- if visible_type?(t)
101
+ @schema.types(@context).each do |n, t|
102
+ if visible_and_reachable_type?(t)
59
103
  vis_types[n] = t
60
104
  end
61
105
  end
@@ -66,8 +110,8 @@ module GraphQL
66
110
  # @return [GraphQL::BaseType, nil] The type named `type_name`, if it exists (else `nil`)
67
111
  def get_type(type_name)
68
112
  @visible_types ||= read_through do |name|
69
- type_defn = @schema.get_type(name)
70
- if type_defn && visible_type?(type_defn)
113
+ type_defn = @schema.get_type(name, @context)
114
+ if type_defn && visible_and_reachable_type?(type_defn)
71
115
  type_defn
72
116
  else
73
117
  nil
@@ -84,7 +128,7 @@ module GraphQL
84
128
 
85
129
  # @return Boolean True if the type is visible and reachable in the schema
86
130
  def reachable_type?(type_name)
87
- type = get_type(type_name)
131
+ type = get_type(type_name) # rubocop:disable Development/ContextIsPassedCop -- `self` is query-aware
88
132
  type && reachable_type_set.include?(type)
89
133
  end
90
134
 
@@ -92,8 +136,8 @@ module GraphQL
92
136
  def get_field(parent_type, field_name)
93
137
  @visible_parent_fields ||= read_through do |type|
94
138
  read_through do |f_name|
95
- field_defn = @schema.get_field(type, f_name)
96
- if field_defn && visible_field?(type, field_defn)
139
+ field_defn = @schema.get_field(type, f_name, @context)
140
+ if field_defn && visible_field?(field_defn, nil, type)
97
141
  field_defn
98
142
  else
99
143
  nil
@@ -106,15 +150,15 @@ module GraphQL
106
150
 
107
151
  # @return [GraphQL::Argument, nil] The argument named `argument_name` on `parent_type`, if it exists and is visible
108
152
  def get_argument(parent_type, argument_name)
109
- argument = parent_type.get_argument(argument_name)
110
- return argument if argument && visible_argument?(argument)
153
+ argument = parent_type.get_argument(argument_name, @context)
154
+ return argument if argument && visible_argument?(argument, @context)
111
155
  end
112
156
 
113
157
  # @return [Array<GraphQL::BaseType>] The types which may be member of `type_defn`
114
158
  def possible_types(type_defn)
115
159
  @visible_possible_types ||= read_through { |type_defn|
116
160
  pt = @schema.possible_types(type_defn, @context)
117
- pt.select { |t| visible_type?(t) }
161
+ pt.select { |t| visible_and_reachable_type?(t) }
118
162
  }
119
163
  @visible_possible_types[type_defn]
120
164
  end
@@ -122,26 +166,31 @@ module GraphQL
122
166
  # @param type_defn [GraphQL::ObjectType, GraphQL::InterfaceType]
123
167
  # @return [Array<GraphQL::Field>] Fields on `type_defn`
124
168
  def fields(type_defn)
125
- @visible_fields ||= read_through { |t| @schema.get_fields(t).each_value.select { |f| visible_field?(t, f) } }
169
+ @visible_fields ||= read_through { |t| @schema.get_fields(t, @context).values }
126
170
  @visible_fields[type_defn]
127
171
  end
128
172
 
129
173
  # @param argument_owner [GraphQL::Field, GraphQL::InputObjectType]
130
174
  # @return [Array<GraphQL::Argument>] Visible arguments on `argument_owner`
131
175
  def arguments(argument_owner)
132
- @visible_arguments ||= read_through { |o| o.arguments.each_value.select { |a| visible_argument?(a) } }
176
+ @visible_arguments ||= read_through { |o| o.arguments(@context).each_value.select { |a| visible_argument?(a) } }
133
177
  @visible_arguments[argument_owner]
134
178
  end
135
179
 
136
180
  # @return [Array<GraphQL::EnumType::EnumValue>] Visible members of `enum_defn`
137
181
  def enum_values(enum_defn)
138
- @visible_enum_values ||= read_through { |e| e.values.each_value.select { |enum_value_defn| visible?(enum_value_defn) } }
139
- @visible_enum_values[enum_defn]
182
+ @visible_enum_arrays ||= read_through { |e| e.enum_values(@context) }
183
+ @visible_enum_arrays[enum_defn]
184
+ end
185
+
186
+ def visible_enum_value?(enum_value, _ctx = nil)
187
+ @visible_enum_values ||= read_through { |ev| visible?(ev) }
188
+ @visible_enum_values[enum_value]
140
189
  end
141
190
 
142
191
  # @return [Array<GraphQL::InterfaceType>] Visible interfaces implemented by `obj_type`
143
192
  def interfaces(obj_type)
144
- @visible_interfaces ||= read_through { |t| t.interfaces(@context).select { |i| visible?(i) } }
193
+ @visible_interfaces ||= read_through { |t| t.interfaces(@context).select { |i| visible_type?(i) } }
145
194
  @visible_interfaces[obj_type]
146
195
  end
147
196
 
@@ -158,25 +207,52 @@ module GraphQL
158
207
  end
159
208
  end
160
209
 
161
- private
162
-
163
- def union_memberships(obj_type)
164
- @unions ||= read_through { |obj_type| @schema.union_memberships(obj_type).select { |u| visible?(u) } }
165
- @unions[obj_type]
166
- end
167
-
168
- def visible_argument?(arg_defn)
169
- visible?(arg_defn) && visible_type?(arg_defn.type.unwrap)
170
- end
171
-
172
- def visible_field?(owner_type, field_defn)
210
+ # @param owner [Class, Module] If provided, confirm that field has the given owner.
211
+ def visible_field?(field_defn, _ctx = nil, owner = field_defn.owner)
173
212
  # This field is visible in its own right
174
213
  visible?(field_defn) &&
175
214
  # This field's return type is visible
176
- visible_type?(field_defn.type.unwrap) &&
215
+ visible_and_reachable_type?(field_defn.type.unwrap) &&
177
216
  # This field is either defined on this object type,
178
217
  # or the interface it's inherited from is also visible
179
- ((field_defn.respond_to?(:owner) && field_defn.owner == owner_type) || field_on_visible_interface?(field_defn, owner_type))
218
+ ((field_defn.respond_to?(:owner) && field_defn.owner == owner) || field_on_visible_interface?(field_defn, owner))
219
+ end
220
+
221
+ def visible_argument?(arg_defn, _ctx = nil)
222
+ visible?(arg_defn) && visible_and_reachable_type?(arg_defn.type.unwrap)
223
+ end
224
+
225
+ def visible_type?(type_defn, _ctx = nil)
226
+ @type_visibility ||= read_through { |type_defn| visible?(type_defn) }
227
+ @type_visibility[type_defn]
228
+ end
229
+
230
+ def visible_type_membership?(type_membership, _ctx = nil)
231
+ visible?(type_membership)
232
+ end
233
+
234
+ private
235
+
236
+ def visible_and_reachable_type?(type_defn)
237
+ @visible_and_reachable_type ||= read_through do |type_defn|
238
+ next false unless visible_type?(type_defn)
239
+ next true if root_type?(type_defn) || type_defn.introspection?
240
+
241
+ if type_defn.kind.union?
242
+ visible_possible_types?(type_defn) && (referenced?(type_defn) || orphan_type?(type_defn))
243
+ elsif type_defn.kind.interface?
244
+ visible_possible_types?(type_defn)
245
+ else
246
+ referenced?(type_defn) || visible_abstract_type?(type_defn)
247
+ end
248
+ end
249
+
250
+ @visible_and_reachable_type[type_defn]
251
+ end
252
+
253
+ def union_memberships(obj_type)
254
+ @unions ||= read_through { |obj_type| @schema.union_memberships(obj_type).select { |u| visible?(u) } }
255
+ @unions[obj_type]
180
256
  end
181
257
 
182
258
  # We need this to tell whether a field was inherited by an interface
@@ -195,10 +271,10 @@ module GraphQL
195
271
  any_interface_has_visible_field = false
196
272
  ints = unfiltered_interfaces(type_defn)
197
273
  ints.each do |interface_type|
198
- if (iface_field_defn = interface_type.get_field(field_defn.graphql_name))
274
+ if (iface_field_defn = interface_type.get_field(field_defn.graphql_name, @context))
199
275
  any_interface_has_field = true
200
276
 
201
- if interfaces(type_defn).include?(interface_type) && visible_field?(interface_type, iface_field_defn)
277
+ if interfaces(type_defn).include?(interface_type) && visible_field?(iface_field_defn, nil, interface_type)
202
278
  any_interface_has_visible_field = true
203
279
  end
204
280
  end
@@ -215,23 +291,6 @@ module GraphQL
215
291
  end
216
292
  end
217
293
 
218
- def visible_type?(type_defn)
219
- @type_visibility ||= read_through do |type_defn|
220
- next false unless visible?(type_defn)
221
- next true if root_type?(type_defn) || type_defn.introspection?
222
-
223
- if type_defn.kind.union?
224
- visible_possible_types?(type_defn) && (referenced?(type_defn) || orphan_type?(type_defn))
225
- elsif type_defn.kind.interface?
226
- visible_possible_types?(type_defn)
227
- else
228
- referenced?(type_defn) || visible_abstract_type?(type_defn)
229
- end
230
- end
231
-
232
- @type_visibility[type_defn]
233
- end
234
-
235
294
  def root_type?(type_defn)
236
295
  @query == type_defn ||
237
296
  @mutation == type_defn ||
@@ -259,7 +318,7 @@ module GraphQL
259
318
  end
260
319
 
261
320
  def visible_possible_types?(type_defn)
262
- possible_types(type_defn).any? { |t| visible_type?(t) }
321
+ possible_types(type_defn).any? { |t| visible_and_reachable_type?(t) }
263
322
  end
264
323
 
265
324
  def visible?(member)
@@ -274,6 +333,7 @@ module GraphQL
274
333
  return @reachable_type_set if defined?(@reachable_type_set)
275
334
 
276
335
  @reachable_type_set = Set.new
336
+ rt_hash = {}
277
337
 
278
338
  unvisited_types = []
279
339
  ['query', 'mutation', 'subscription'].each do |op_name|
@@ -283,16 +343,16 @@ module GraphQL
283
343
  unvisited_types.concat(@schema.introspection_system.types.values)
284
344
 
285
345
  directives.each do |dir_class|
286
- dir_class.arguments.values.each do |arg_defn|
346
+ arguments(dir_class).each do |arg_defn|
287
347
  arg_t = arg_defn.type.unwrap
288
- if get_type(arg_t.graphql_name)
348
+ if get_type(arg_t.graphql_name) # rubocop:disable Development/ContextIsPassedCop -- `self` is query-aware
289
349
  unvisited_types << arg_t
290
350
  end
291
351
  end
292
352
  end
293
353
 
294
354
  @schema.orphan_types.each do |orphan_type|
295
- if get_type(orphan_type.graphql_name)
355
+ if get_type(orphan_type.graphql_name) == orphan_type # rubocop:disable Development/ContextIsPassedCop -- `self` is query-aware
296
356
  unvisited_types << orphan_type
297
357
  end
298
358
  end
@@ -300,6 +360,10 @@ module GraphQL
300
360
  until unvisited_types.empty?
301
361
  type = unvisited_types.pop
302
362
  if @reachable_type_set.add?(type)
363
+ type_by_name = rt_hash[type.graphql_name] ||= type
364
+ if type_by_name != type
365
+ raise DuplicateNamesError, "Found two visible type definitions for `#{type.graphql_name}`: #{type.inspect}, #{type_by_name.inspect}"
366
+ end
303
367
  if type.kind.input_object?
304
368
  # recurse into visible arguments
305
369
  arguments(type).each do |argument|
@@ -92,6 +92,8 @@ module GraphQL
92
92
  end
93
93
  end
94
94
 
95
+ class DuplicateNamesError < GraphQL::Error; end
96
+
95
97
  class UnresolvedLateBoundTypeError < GraphQL::Error
96
98
  attr_reader :type
97
99
  def initialize(type:)
@@ -996,16 +998,58 @@ module GraphQL
996
998
  # Build a map of `{ name => type }` and return it
997
999
  # @return [Hash<String => Class>] A dictionary of type classes by their GraphQL name
998
1000
  # @see get_type Which is more efficient for finding _one type_ by name, because it doesn't merge hashes.
999
- def types
1000
- non_introspection_types.merge(introspection_system.types)
1001
+ def types(context = GraphQL::Query::NullContext)
1002
+ all_types = non_introspection_types.merge(introspection_system.types)
1003
+ visible_types = {}
1004
+ all_types.each do |k, v|
1005
+ visible_types[k] =if v.is_a?(Array)
1006
+ visible_t = nil
1007
+ v.each do |t|
1008
+ if t.visible?(context)
1009
+ if visible_t.nil?
1010
+ visible_t = t
1011
+ else
1012
+ raise DuplicateNamesError, "Found two visible type definitions for `#{k}`: #{visible_t.inspect}, #{t.inspect}"
1013
+ end
1014
+ end
1015
+ end
1016
+ visible_t
1017
+ else
1018
+ v
1019
+ end
1020
+ end
1021
+ visible_types
1001
1022
  end
1002
1023
 
1003
1024
  # @param type_name [String]
1004
1025
  # @return [Module, nil] A type, or nil if there's no type called `type_name`
1005
- def get_type(type_name)
1006
- own_types[type_name] ||
1007
- introspection_system.types[type_name] ||
1008
- find_inherited_value(:types, EMPTY_HASH)[type_name]
1026
+ def get_type(type_name, context = GraphQL::Query::NullContext)
1027
+ local_entry = own_types[type_name]
1028
+ type_defn = case local_entry
1029
+ when nil
1030
+ nil
1031
+ when Array
1032
+ visible_t = nil
1033
+ warden = Warden.from_context(context)
1034
+ local_entry.each do |t|
1035
+ if warden.visible_type?(t, context)
1036
+ if visible_t.nil?
1037
+ visible_t = t
1038
+ else
1039
+ raise DuplicateNamesError, "Found two visible type definitions for `#{type_name}`: #{visible_t.inspect}, #{t.inspect}"
1040
+ end
1041
+ end
1042
+ end
1043
+ visible_t
1044
+ when Module
1045
+ local_entry
1046
+ else
1047
+ raise "Invariant: unexpected own_types[#{type_name.inspect}]: #{local_entry.inspect}"
1048
+ end
1049
+
1050
+ type_defn ||
1051
+ introspection_system.types[type_name] || # todo context-specific introspection?
1052
+ (superclass.respond_to?(:get_type) ? superclass.get_type(type_name, context) : nil)
1009
1053
  end
1010
1054
 
1011
1055
  # @api private
@@ -1182,19 +1226,19 @@ module GraphQL
1182
1226
  GraphQL::Schema::TypeExpression.build_type(type_owner, ast_node)
1183
1227
  end
1184
1228
 
1185
- def get_field(type_or_name, field_name)
1229
+ def get_field(type_or_name, field_name, context = GraphQL::Query::NullContext)
1186
1230
  parent_type = case type_or_name
1187
1231
  when LateBoundType
1188
- get_type(type_or_name.name)
1232
+ get_type(type_or_name.name, context)
1189
1233
  when String
1190
- get_type(type_or_name)
1234
+ get_type(type_or_name, context)
1191
1235
  when Module
1192
1236
  type_or_name
1193
1237
  else
1194
1238
  raise ArgumentError, "unexpected field owner for #{field_name.inspect}: #{type_or_name.inspect} (#{type_or_name.class})"
1195
1239
  end
1196
1240
 
1197
- if parent_type.kind.fields? && (field = parent_type.get_field(field_name))
1241
+ if parent_type.kind.fields? && (field = parent_type.get_field(field_name, context))
1198
1242
  field
1199
1243
  elsif parent_type == query && (entry_point_field = introspection_system.entry_point(name: field_name))
1200
1244
  entry_point_field
@@ -1205,8 +1249,8 @@ module GraphQL
1205
1249
  end
1206
1250
  end
1207
1251
 
1208
- def get_fields(type)
1209
- type.fields
1252
+ def get_fields(type, context = GraphQL::Query::NullContext)
1253
+ type.fields(context)
1210
1254
  end
1211
1255
 
1212
1256
  def introspection(new_introspection_namespace = nil)
@@ -1405,7 +1449,6 @@ module GraphQL
1405
1449
  if new_orphan_types.any?
1406
1450
  new_orphan_types = new_orphan_types.flatten
1407
1451
  add_type_and_traverse(new_orphan_types, root: false)
1408
- @orphan_types = new_orphan_types
1409
1452
  own_orphan_types.concat(new_orphan_types.flatten)
1410
1453
  end
1411
1454
 
@@ -1715,7 +1758,7 @@ module GraphQL
1715
1758
  if subscription.singleton_class.ancestors.include?(Subscriptions::SubscriptionRoot)
1716
1759
  GraphQL::Deprecation.warn("`extend Subscriptions::SubscriptionRoot` is no longer required; you may remove it from #{self}'s `subscription` root type (#{subscription}).")
1717
1760
  else
1718
- subscription.fields.each do |name, field|
1761
+ subscription.all_field_definitions.each do |field|
1719
1762
  field.extension(Subscriptions::DefaultSubscriptionResolveExtension)
1720
1763
  end
1721
1764
  end
@@ -1737,7 +1780,36 @@ module GraphQL
1737
1780
  end
1738
1781
  new_types = Array(t)
1739
1782
  addition = Schema::Addition.new(schema: self, own_types: own_types, new_types: new_types)
1740
- own_types.merge!(addition.types)
1783
+ addition.types.each do |name, types_entry| # rubocop:disable Development/ContextIsPassedCop -- build-time, not query-time
1784
+ if (prev_entry = own_types[name])
1785
+ prev_entries = case prev_entry
1786
+ when Array
1787
+ prev_entry
1788
+ when Module
1789
+ own_types[name] = [prev_entry]
1790
+ else
1791
+ raise "Invariant: unexpected prev_entry at #{name.inspect} when adding #{t.inspect}"
1792
+ end
1793
+
1794
+ case types_entry
1795
+ when Array
1796
+ prev_entries.concat(types_entry)
1797
+ prev_entries.uniq! # in case any are being re-visited
1798
+ when Module
1799
+ if !prev_entries.include?(types_entry)
1800
+ prev_entries << types_entry
1801
+ end
1802
+ else
1803
+ raise "Invariant: unexpected types_entry at #{name} when adding #{t.inspect}"
1804
+ end
1805
+ else
1806
+ if types_entry.is_a?(Array)
1807
+ types_entry.uniq!
1808
+ end
1809
+ own_types[name] = types_entry
1810
+ end
1811
+ end
1812
+
1741
1813
  own_possible_types.merge!(addition.possible_types) { |key, old_val, new_val| old_val + new_val }
1742
1814
  own_union_memberships.merge!(addition.union_memberships)
1743
1815
 
@@ -94,7 +94,7 @@ module GraphQL
94
94
 
95
95
  def on_field(node, parent)
96
96
  parent_type = @object_types.last
97
- field_definition = @schema.get_field(parent_type, node.name)
97
+ field_definition = @schema.get_field(parent_type, node.name, @context.query.context)
98
98
  @field_definitions.push(field_definition)
99
99
  if !field_definition.nil?
100
100
  next_object_type = field_definition.type.unwrap
@@ -120,14 +120,14 @@ module GraphQL
120
120
  argument_defn = if (arg = @argument_definitions.last)
121
121
  arg_type = arg.type.unwrap
122
122
  if arg_type.kind.input_object?
123
- arg_type.arguments[node.name]
123
+ @context.warden.get_argument(arg_type, node.name)
124
124
  else
125
125
  nil
126
126
  end
127
127
  elsif (directive_defn = @directive_definitions.last)
128
- directive_defn.arguments[node.name]
128
+ @context.warden.get_argument(directive_defn, node.name)
129
129
  elsif (field_defn = @field_definitions.last)
130
- field_defn.arguments[node.name]
130
+ @context.warden.get_argument(field_defn, node.name)
131
131
  else
132
132
  nil
133
133
  end
@@ -187,7 +187,7 @@ module GraphQL
187
187
 
188
188
  def on_fragment_with_type(node)
189
189
  object_type = if node.type
190
- @schema.get_type(node.type.name)
190
+ @context.warden.get_type(node.type.name)
191
191
  else
192
192
  @object_types.last
193
193
  end
@@ -70,7 +70,6 @@ module GraphQL
70
70
  @dependency_map ||= resolve_dependencies(&block)
71
71
  end
72
72
 
73
-
74
73
  # Map definition AST nodes to the definition AST nodes they depend on.
75
74
  # Expose circular dependencies.
76
75
  class DependencyMap
@@ -95,7 +95,7 @@ module GraphQL
95
95
  def required_input_fields_are_present(type, ast_node)
96
96
  # TODO - would be nice to use these to create an error message so the caller knows
97
97
  # that required fields are missing
98
- required_field_names = type.arguments.each_value
98
+ required_field_names = @warden.arguments(type)
99
99
  .select { |argument| argument.type.kind.non_null? && @warden.get_argument(type, argument.name) }
100
100
  .map(&:name)
101
101
 
@@ -15,7 +15,7 @@ module GraphQL
15
15
  if @context.schema.error_bubbling || context.errors.none? { |err| err.path.take(@path.size) == @path }
16
16
  parent_defn = parent_definition(parent)
17
17
 
18
- if parent_defn && (arg_defn = parent_defn.arguments[node.name])
18
+ if parent_defn && (arg_defn = context.warden.get_argument(parent_defn, node.name))
19
19
  validation_result = context.validate_literal(node.value, arg_defn.type)
20
20
  if !validation_result.valid?
21
21
  kind_of_node = node_type(parent)
@@ -50,15 +50,15 @@ module GraphQL
50
50
  @arg_conflicts = nil
51
51
 
52
52
  yield
53
- # don't initialize these if they weren't initialized in the block:
54
- @field_conflicts && @field_conflicts.each_value { |error| add_error(error) }
55
- @arg_conflicts && @arg_conflicts.each_value { |error| add_error(error) }
53
+
54
+ field_conflicts.each_value { |error| add_error(error) }
55
+ arg_conflicts.each_value { |error| add_error(error) }
56
56
  end
57
57
 
58
58
  def conflicts_within_selection_set(node, parent_type)
59
59
  return if parent_type.nil?
60
60
 
61
- fields, fragment_spreads = fields_and_fragments_from_selection(node, owner_type: parent_type, parents: nil)
61
+ fields, fragment_spreads = fields_and_fragments_from_selection(node, owner_type: parent_type, parents: [])
62
62
 
63
63
  # (A) Find find all conflicts "within" the fields of this selection set.
64
64
  find_conflicts_within(fields)
@@ -198,14 +198,10 @@ module GraphQL
198
198
  response_keys.each do |key, fields|
199
199
  next if fields.size < 2
200
200
  # find conflicts within nodes
201
- i = 0
202
- while i < fields.size
203
- j = i + 1
204
- while j < fields.size
201
+ for i in 0..fields.size - 1
202
+ for j in i + 1..fields.size - 1
205
203
  find_conflict(key, fields[i], fields[j])
206
- j += 1
207
204
  end
208
- i += 1
209
205
  end
210
206
  end
211
207
  end
@@ -247,9 +243,7 @@ module GraphQL
247
243
  end
248
244
 
249
245
  def find_conflicts_between_sub_selection_sets(field1, field2, mutually_exclusive:)
250
- return if field1.definition.nil? ||
251
- field2.definition.nil? ||
252
- (field1.node.selections.empty? && field2.node.selections.empty?)
246
+ return if field1.definition.nil? || field2.definition.nil?
253
247
 
254
248
  return_type1 = field1.definition.type.unwrap
255
249
  return_type2 = field2.definition.type.unwrap
@@ -329,7 +323,6 @@ module GraphQL
329
323
  if node.selections.empty?
330
324
  NO_SELECTIONS
331
325
  else
332
- parents ||= []
333
326
  fields, fragment_spreads = find_fields_and_fragments(node.selections, owner_type: owner_type, parents: parents, fields: [], fragment_spreads: [])
334
327
  response_keys = fields.group_by { |f| f.node.alias || f.node.name }
335
328
  [response_keys, fragment_spreads]
@@ -340,7 +333,7 @@ module GraphQL
340
333
  selections.each do |node|
341
334
  case node
342
335
  when GraphQL::Language::Nodes::Field
343
- definition = context.schema.get_field(owner_type, node.name)
336
+ definition = context.query.get_field(owner_type, node.name)
344
337
  fields << Field.new(node, definition, owner_type, parents)
345
338
  when GraphQL::Language::Nodes::InlineFragment
346
339
  fragment_type = node.type ? context.warden.get_type(node.type.name) : owner_type
@@ -16,10 +16,8 @@ module GraphQL
16
16
  private
17
17
 
18
18
  def assert_required_args(ast_node, defn)
19
- args = defn.arguments
20
- return if args.empty?
21
19
  present_argument_names = ast_node.arguments.map(&:name)
22
- required_argument_names = defn.arguments.each_value
20
+ required_argument_names = context.warden.arguments(defn)
23
21
  .select { |a| a.type.kind.non_null? && !a.default_value? && context.warden.get_argument(defn, a.name) }
24
22
  .map(&:name)
25
23