graphql 1.12.21 → 1.13.2

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.
Files changed (123) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/core.rb +3 -1
  3. data/lib/generators/graphql/install_generator.rb +9 -2
  4. data/lib/generators/graphql/mutation_generator.rb +1 -1
  5. data/lib/generators/graphql/type_generator.rb +0 -1
  6. data/lib/graphql/analysis/ast/field_usage.rb +2 -2
  7. data/lib/graphql/analysis/ast/query_complexity.rb +10 -14
  8. data/lib/graphql/analysis/ast/visitor.rb +4 -4
  9. data/lib/graphql/backtrace/table.rb +1 -1
  10. data/lib/graphql/base_type.rb +4 -2
  11. data/lib/graphql/boolean_type.rb +1 -1
  12. data/lib/graphql/dataloader.rb +55 -22
  13. data/lib/graphql/directive/deprecated_directive.rb +1 -1
  14. data/lib/graphql/directive/include_directive.rb +1 -1
  15. data/lib/graphql/directive/skip_directive.rb +1 -1
  16. data/lib/graphql/directive.rb +0 -4
  17. data/lib/graphql/enum_type.rb +5 -1
  18. data/lib/graphql/execution/errors.rb +1 -0
  19. data/lib/graphql/execution/interpreter/arguments.rb +1 -1
  20. data/lib/graphql/execution/interpreter/arguments_cache.rb +2 -2
  21. data/lib/graphql/execution/interpreter/runtime.rb +31 -19
  22. data/lib/graphql/execution/lookahead.rb +2 -2
  23. data/lib/graphql/execution/multiplex.rb +4 -1
  24. data/lib/graphql/float_type.rb +1 -1
  25. data/lib/graphql/id_type.rb +1 -1
  26. data/lib/graphql/int_type.rb +1 -1
  27. data/lib/graphql/introspection/directive_type.rb +1 -1
  28. data/lib/graphql/introspection/entry_points.rb +2 -2
  29. data/lib/graphql/introspection/enum_value_type.rb +2 -2
  30. data/lib/graphql/introspection/field_type.rb +2 -2
  31. data/lib/graphql/introspection/input_value_type.rb +4 -4
  32. data/lib/graphql/introspection/schema_type.rb +2 -2
  33. data/lib/graphql/introspection/type_type.rb +10 -10
  34. data/lib/graphql/language/block_string.rb +2 -6
  35. data/lib/graphql/language/document_from_schema_definition.rb +4 -2
  36. data/lib/graphql/language/lexer.rb +0 -3
  37. data/lib/graphql/language/lexer.rl +0 -4
  38. data/lib/graphql/language/nodes.rb +12 -2
  39. data/lib/graphql/language/parser.rb +442 -434
  40. data/lib/graphql/language/parser.y +5 -4
  41. data/lib/graphql/language/printer.rb +6 -1
  42. data/lib/graphql/language/sanitized_printer.rb +5 -5
  43. data/lib/graphql/language/token.rb +0 -4
  44. data/lib/graphql/name_validator.rb +0 -4
  45. data/lib/graphql/query/arguments.rb +1 -1
  46. data/lib/graphql/query/arguments_cache.rb +1 -1
  47. data/lib/graphql/query/context.rb +15 -2
  48. data/lib/graphql/query/literal_input.rb +1 -1
  49. data/lib/graphql/query/null_context.rb +12 -7
  50. data/lib/graphql/query/serial_execution/field_resolution.rb +1 -1
  51. data/lib/graphql/query/variables.rb +5 -1
  52. data/lib/graphql/relay/edges_instrumentation.rb +0 -1
  53. data/lib/graphql/relay/global_id_resolve.rb +1 -1
  54. data/lib/graphql/relay/page_info.rb +1 -1
  55. data/lib/graphql/rubocop/graphql/base_cop.rb +36 -0
  56. data/lib/graphql/rubocop/graphql/default_null_true.rb +43 -0
  57. data/lib/graphql/rubocop/graphql/default_required_true.rb +43 -0
  58. data/lib/graphql/rubocop.rb +4 -0
  59. data/lib/graphql/schema/addition.rb +37 -28
  60. data/lib/graphql/schema/argument.rb +8 -6
  61. data/lib/graphql/schema/build_from_definition.rb +5 -5
  62. data/lib/graphql/schema/directive/feature.rb +1 -1
  63. data/lib/graphql/schema/directive/flagged.rb +2 -2
  64. data/lib/graphql/schema/directive/include.rb +1 -1
  65. data/lib/graphql/schema/directive/skip.rb +1 -1
  66. data/lib/graphql/schema/directive/transform.rb +1 -1
  67. data/lib/graphql/schema/directive.rb +7 -3
  68. data/lib/graphql/schema/enum.rb +60 -10
  69. data/lib/graphql/schema/enum_value.rb +6 -0
  70. data/lib/graphql/schema/field/connection_extension.rb +1 -1
  71. data/lib/graphql/schema/field.rb +126 -38
  72. data/lib/graphql/schema/field_extension.rb +52 -2
  73. data/lib/graphql/schema/find_inherited_value.rb +1 -0
  74. data/lib/graphql/schema/finder.rb +5 -5
  75. data/lib/graphql/schema/input_object.rb +8 -5
  76. data/lib/graphql/schema/interface.rb +11 -20
  77. data/lib/graphql/schema/introspection_system.rb +1 -1
  78. data/lib/graphql/schema/list.rb +3 -1
  79. data/lib/graphql/schema/member/accepts_definition.rb +15 -3
  80. data/lib/graphql/schema/member/build_type.rb +0 -4
  81. data/lib/graphql/schema/member/cached_graphql_definition.rb +29 -2
  82. data/lib/graphql/schema/member/has_arguments.rb +55 -13
  83. data/lib/graphql/schema/member/has_deprecation_reason.rb +1 -1
  84. data/lib/graphql/schema/member/has_fields.rb +76 -18
  85. data/lib/graphql/schema/member/has_interfaces.rb +90 -0
  86. data/lib/graphql/schema/member.rb +1 -0
  87. data/lib/graphql/schema/non_null.rb +3 -1
  88. data/lib/graphql/schema/object.rb +10 -75
  89. data/lib/graphql/schema/printer.rb +1 -1
  90. data/lib/graphql/schema/relay_classic_mutation.rb +37 -3
  91. data/lib/graphql/schema/resolver/has_payload_type.rb +27 -2
  92. data/lib/graphql/schema/resolver.rb +37 -17
  93. data/lib/graphql/schema/scalar.rb +2 -0
  94. data/lib/graphql/schema/subscription.rb +11 -1
  95. data/lib/graphql/schema/traversal.rb +1 -1
  96. data/lib/graphql/schema/type_expression.rb +1 -1
  97. data/lib/graphql/schema/type_membership.rb +18 -4
  98. data/lib/graphql/schema/union.rb +8 -1
  99. data/lib/graphql/schema/validator/format_validator.rb +0 -4
  100. data/lib/graphql/schema/validator/numericality_validator.rb +1 -0
  101. data/lib/graphql/schema/validator.rb +4 -7
  102. data/lib/graphql/schema/warden.rb +116 -52
  103. data/lib/graphql/schema.rb +106 -22
  104. data/lib/graphql/static_validation/base_visitor.rb +5 -5
  105. data/lib/graphql/static_validation/definition_dependencies.rb +0 -1
  106. data/lib/graphql/static_validation/literal_validator.rb +1 -1
  107. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
  108. data/lib/graphql/static_validation/rules/fields_will_merge.rb +15 -8
  109. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +3 -1
  110. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +4 -4
  111. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +7 -7
  112. data/lib/graphql/string_type.rb +1 -1
  113. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +8 -4
  114. data/lib/graphql/subscriptions/event.rb +20 -12
  115. data/lib/graphql/subscriptions.rb +17 -19
  116. data/lib/graphql/types/relay/connection_behaviors.rb +26 -9
  117. data/lib/graphql/types/relay/default_relay.rb +5 -1
  118. data/lib/graphql/types/relay/edge_behaviors.rb +13 -2
  119. data/lib/graphql/types/relay/has_node_field.rb +1 -1
  120. data/lib/graphql/types/relay/has_nodes_field.rb +1 -1
  121. data/lib/graphql/version.rb +1 -1
  122. data/lib/graphql.rb +10 -32
  123. metadata +10 -5
@@ -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:)
@@ -843,7 +845,7 @@ module GraphQL
843
845
  # - Cause the Schema instance to be created, if it hasn't been created yet
844
846
  # - Delegate to that instance
845
847
  # Eventually, the methods will be moved into this class, removing the need for the singleton.
846
- def_delegators :graphql_definition,
848
+ def_delegators :deprecated_graphql_definition,
847
849
  # Execution
848
850
  :execution_strategy_for_operation,
849
851
  # Configuration
@@ -852,6 +854,10 @@ module GraphQL
852
854
  :id_from_object=, :object_from_id=,
853
855
  :remove_handler
854
856
 
857
+ def deprecated_graphql_definition
858
+ graphql_definition(silence_deprecation_warning: true)
859
+ end
860
+
855
861
  # @return [GraphQL::Subscriptions]
856
862
  attr_accessor :subscriptions
857
863
 
@@ -894,8 +900,15 @@ module GraphQL
894
900
  @find_cache[path] ||= @finder.find(path)
895
901
  end
896
902
 
897
- def graphql_definition
898
- @graphql_definition ||= to_graphql
903
+ def graphql_definition(silence_deprecation_warning: false)
904
+ @graphql_definition ||= begin
905
+ unless silence_deprecation_warning
906
+ message = "Legacy `.graphql_definition` objects are deprecated and will be removed in GraphQL-Ruby 2.0. Use a class-based definition instead."
907
+ caller_message = "\n\nCalled on #{self.inspect} from:\n #{caller(1, 25).map { |l| " #{l}" }.join("\n")}"
908
+ GraphQL::Deprecation.warn(message + caller_message)
909
+ end
910
+ to_graphql(silence_deprecation_warning: silence_deprecation_warning)
911
+ end
899
912
  end
900
913
 
901
914
  def default_filter
@@ -927,19 +940,20 @@ module GraphQL
927
940
  find_inherited_value(:plugins, EMPTY_ARRAY) + own_plugins
928
941
  end
929
942
 
943
+ prepend Schema::Member::CachedGraphQLDefinition::DeprecatedToGraphQL
930
944
  def to_graphql
931
945
  schema_defn = self.new
932
946
  schema_defn.raise_definition_error = true
933
- schema_defn.query = query && query.graphql_definition
934
- schema_defn.mutation = mutation && mutation.graphql_definition
935
- schema_defn.subscription = subscription && subscription.graphql_definition
947
+ schema_defn.query = query && query.graphql_definition(silence_deprecation_warning: true)
948
+ schema_defn.mutation = mutation && mutation.graphql_definition(silence_deprecation_warning: true)
949
+ schema_defn.subscription = subscription && subscription.graphql_definition(silence_deprecation_warning: true)
936
950
  schema_defn.validate_timeout = validate_timeout
937
951
  schema_defn.validate_max_errors = validate_max_errors
938
952
  schema_defn.max_complexity = max_complexity
939
953
  schema_defn.error_bubbling = error_bubbling
940
954
  schema_defn.max_depth = max_depth
941
955
  schema_defn.default_max_page_size = default_max_page_size
942
- schema_defn.orphan_types = orphan_types.map(&:graphql_definition)
956
+ schema_defn.orphan_types = orphan_types.map { |t| t.graphql_definition(silence_deprecation_warning: true) }
943
957
  schema_defn.disable_introspection_entry_points = disable_introspection_entry_points?
944
958
  schema_defn.disable_schema_introspection_entry_point = disable_schema_introspection_entry_point?
945
959
  schema_defn.disable_type_introspection_entry_point = disable_type_introspection_entry_point?
@@ -996,16 +1010,58 @@ module GraphQL
996
1010
  # Build a map of `{ name => type }` and return it
997
1011
  # @return [Hash<String => Class>] A dictionary of type classes by their GraphQL name
998
1012
  # @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)
1013
+ def types(context = GraphQL::Query::NullContext)
1014
+ all_types = non_introspection_types.merge(introspection_system.types)
1015
+ visible_types = {}
1016
+ all_types.each do |k, v|
1017
+ visible_types[k] =if v.is_a?(Array)
1018
+ visible_t = nil
1019
+ v.each do |t|
1020
+ if t.visible?(context)
1021
+ if visible_t.nil?
1022
+ visible_t = t
1023
+ else
1024
+ raise DuplicateNamesError, "Found two visible type definitions for `#{k}`: #{visible_t.inspect}, #{t.inspect}"
1025
+ end
1026
+ end
1027
+ end
1028
+ visible_t
1029
+ else
1030
+ v
1031
+ end
1032
+ end
1033
+ visible_types
1001
1034
  end
1002
1035
 
1003
1036
  # @param type_name [String]
1004
1037
  # @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]
1038
+ def get_type(type_name, context = GraphQL::Query::NullContext)
1039
+ local_entry = own_types[type_name]
1040
+ type_defn = case local_entry
1041
+ when nil
1042
+ nil
1043
+ when Array
1044
+ visible_t = nil
1045
+ warden = Warden.from_context(context)
1046
+ local_entry.each do |t|
1047
+ if warden.visible_type?(t, context)
1048
+ if visible_t.nil?
1049
+ visible_t = t
1050
+ else
1051
+ raise DuplicateNamesError, "Found two visible type definitions for `#{type_name}`: #{visible_t.inspect}, #{t.inspect}"
1052
+ end
1053
+ end
1054
+ end
1055
+ visible_t
1056
+ when Module
1057
+ local_entry
1058
+ else
1059
+ raise "Invariant: unexpected own_types[#{type_name.inspect}]: #{local_entry.inspect}"
1060
+ end
1061
+
1062
+ type_defn ||
1063
+ introspection_system.types[type_name] || # todo context-specific introspection?
1064
+ (superclass.respond_to?(:get_type) ? superclass.get_type(type_name, context) : nil)
1009
1065
  end
1010
1066
 
1011
1067
  # @api private
@@ -1182,19 +1238,19 @@ module GraphQL
1182
1238
  GraphQL::Schema::TypeExpression.build_type(type_owner, ast_node)
1183
1239
  end
1184
1240
 
1185
- def get_field(type_or_name, field_name)
1241
+ def get_field(type_or_name, field_name, context = GraphQL::Query::NullContext)
1186
1242
  parent_type = case type_or_name
1187
1243
  when LateBoundType
1188
- get_type(type_or_name.name)
1244
+ get_type(type_or_name.name, context)
1189
1245
  when String
1190
- get_type(type_or_name)
1246
+ get_type(type_or_name, context)
1191
1247
  when Module
1192
1248
  type_or_name
1193
1249
  else
1194
1250
  raise ArgumentError, "unexpected field owner for #{field_name.inspect}: #{type_or_name.inspect} (#{type_or_name.class})"
1195
1251
  end
1196
1252
 
1197
- if parent_type.kind.fields? && (field = parent_type.get_field(field_name))
1253
+ if parent_type.kind.fields? && (field = parent_type.get_field(field_name, context))
1198
1254
  field
1199
1255
  elsif parent_type == query && (entry_point_field = introspection_system.entry_point(name: field_name))
1200
1256
  entry_point_field
@@ -1205,8 +1261,8 @@ module GraphQL
1205
1261
  end
1206
1262
  end
1207
1263
 
1208
- def get_fields(type)
1209
- type.fields
1264
+ def get_fields(type, context = GraphQL::Query::NullContext)
1265
+ type.fields(context)
1210
1266
  end
1211
1267
 
1212
1268
  def introspection(new_introspection_namespace = nil)
@@ -1405,7 +1461,6 @@ module GraphQL
1405
1461
  if new_orphan_types.any?
1406
1462
  new_orphan_types = new_orphan_types.flatten
1407
1463
  add_type_and_traverse(new_orphan_types, root: false)
1408
- @orphan_types = new_orphan_types
1409
1464
  own_orphan_types.concat(new_orphan_types.flatten)
1410
1465
  end
1411
1466
 
@@ -1715,7 +1770,7 @@ module GraphQL
1715
1770
  if subscription.singleton_class.ancestors.include?(Subscriptions::SubscriptionRoot)
1716
1771
  GraphQL::Deprecation.warn("`extend Subscriptions::SubscriptionRoot` is no longer required; you may remove it from #{self}'s `subscription` root type (#{subscription}).")
1717
1772
  else
1718
- subscription.fields.each do |name, field|
1773
+ subscription.all_field_definitions.each do |field|
1719
1774
  field.extension(Subscriptions::DefaultSubscriptionResolveExtension)
1720
1775
  end
1721
1776
  end
@@ -1737,7 +1792,36 @@ module GraphQL
1737
1792
  end
1738
1793
  new_types = Array(t)
1739
1794
  addition = Schema::Addition.new(schema: self, own_types: own_types, new_types: new_types)
1740
- own_types.merge!(addition.types)
1795
+ addition.types.each do |name, types_entry| # rubocop:disable Development/ContextIsPassedCop -- build-time, not query-time
1796
+ if (prev_entry = own_types[name])
1797
+ prev_entries = case prev_entry
1798
+ when Array
1799
+ prev_entry
1800
+ when Module
1801
+ own_types[name] = [prev_entry]
1802
+ else
1803
+ raise "Invariant: unexpected prev_entry at #{name.inspect} when adding #{t.inspect}"
1804
+ end
1805
+
1806
+ case types_entry
1807
+ when Array
1808
+ prev_entries.concat(types_entry)
1809
+ prev_entries.uniq! # in case any are being re-visited
1810
+ when Module
1811
+ if !prev_entries.include?(types_entry)
1812
+ prev_entries << types_entry
1813
+ end
1814
+ else
1815
+ raise "Invariant: unexpected types_entry at #{name} when adding #{t.inspect}"
1816
+ end
1817
+ else
1818
+ if types_entry.is_a?(Array)
1819
+ types_entry.uniq!
1820
+ end
1821
+ own_types[name] = types_entry
1822
+ end
1823
+ end
1824
+
1741
1825
  own_possible_types.merge!(addition.possible_types) { |key, old_val, new_val| old_val + new_val }
1742
1826
  own_union_memberships.merge!(addition.union_memberships)
1743
1827
 
@@ -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
-
54
- field_conflicts.each_value { |error| add_error(error) }
55
- arg_conflicts.each_value { |error| add_error(error) }
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) }
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: [])
61
+ fields, fragment_spreads = fields_and_fragments_from_selection(node, owner_type: parent_type, parents: nil)
62
62
 
63
63
  # (A) Find find all conflicts "within" the fields of this selection set.
64
64
  find_conflicts_within(fields)
@@ -198,10 +198,14 @@ module GraphQL
198
198
  response_keys.each do |key, fields|
199
199
  next if fields.size < 2
200
200
  # find conflicts within nodes
201
- for i in 0..fields.size - 1
202
- for j in i + 1..fields.size - 1
201
+ i = 0
202
+ while i < fields.size
203
+ j = i + 1
204
+ while j < fields.size
203
205
  find_conflict(key, fields[i], fields[j])
206
+ j += 1
204
207
  end
208
+ i += 1
205
209
  end
206
210
  end
207
211
  end
@@ -243,7 +247,9 @@ module GraphQL
243
247
  end
244
248
 
245
249
  def find_conflicts_between_sub_selection_sets(field1, field2, mutually_exclusive:)
246
- return if field1.definition.nil? || field2.definition.nil?
250
+ return if field1.definition.nil? ||
251
+ field2.definition.nil? ||
252
+ (field1.node.selections.empty? && field2.node.selections.empty?)
247
253
 
248
254
  return_type1 = field1.definition.type.unwrap
249
255
  return_type2 = field2.definition.type.unwrap
@@ -323,6 +329,7 @@ module GraphQL
323
329
  if node.selections.empty?
324
330
  NO_SELECTIONS
325
331
  else
332
+ parents ||= []
326
333
  fields, fragment_spreads = find_fields_and_fragments(node.selections, owner_type: owner_type, parents: parents, fields: [], fragment_spreads: [])
327
334
  response_keys = fields.group_by { |f| f.node.alias || f.node.name }
328
335
  [response_keys, fragment_spreads]
@@ -333,7 +340,7 @@ module GraphQL
333
340
  selections.each do |node|
334
341
  case node
335
342
  when GraphQL::Language::Nodes::Field
336
- definition = context.schema.get_field(owner_type, node.name)
343
+ definition = context.query.get_field(owner_type, node.name)
337
344
  fields << Field.new(node, definition, owner_type, parents)
338
345
  when GraphQL::Language::Nodes::InlineFragment
339
346
  fragment_type = node.type ? context.warden.get_type(node.type.name) : owner_type
@@ -16,8 +16,10 @@ module GraphQL
16
16
  private
17
17
 
18
18
  def assert_required_args(ast_node, defn)
19
+ args = defn.arguments(context.query.context)
20
+ return if args.empty?
19
21
  present_argument_names = ast_node.arguments.map(&:name)
20
- required_argument_names = defn.arguments.each_value
22
+ required_argument_names = context.warden.arguments(defn)
21
23
  .select { |a| a.type.kind.non_null? && !a.default_value? && context.warden.get_argument(defn, a.name) }
22
24
  .map(&:name)
23
25