graphql 2.1.1 → 2.1.5

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 (34) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql/analysis/ast/query_depth.rb +7 -2
  3. data/lib/graphql/dataloader.rb +29 -10
  4. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +1 -1
  5. data/lib/graphql/execution/interpreter/runtime.rb +7 -26
  6. data/lib/graphql/execution/lookahead.rb +87 -20
  7. data/lib/graphql/load_application_object_failed_error.rb +5 -1
  8. data/lib/graphql/pagination/connection.rb +15 -10
  9. data/lib/graphql/pagination/mongoid_relation_connection.rb +1 -2
  10. data/lib/graphql/query/context.rb +4 -0
  11. data/lib/graphql/query/null_context.rb +2 -10
  12. data/lib/graphql/query.rb +10 -0
  13. data/lib/graphql/schema/build_from_definition.rb +0 -11
  14. data/lib/graphql/schema/directive/one_of.rb +12 -0
  15. data/lib/graphql/schema/directive.rb +1 -1
  16. data/lib/graphql/schema/enum.rb +2 -2
  17. data/lib/graphql/schema/field/scope_extension.rb +4 -3
  18. data/lib/graphql/schema/field.rb +2 -2
  19. data/lib/graphql/schema/has_single_input_argument.rb +2 -2
  20. data/lib/graphql/schema/input_object.rb +1 -1
  21. data/lib/graphql/schema/interface.rb +10 -10
  22. data/lib/graphql/schema/loader.rb +0 -2
  23. data/lib/graphql/schema/member/has_arguments.rb +47 -36
  24. data/lib/graphql/schema/member/has_fields.rb +4 -4
  25. data/lib/graphql/schema/member/has_interfaces.rb +2 -2
  26. data/lib/graphql/schema/member/validates_input.rb +3 -3
  27. data/lib/graphql/schema/resolver.rb +3 -3
  28. data/lib/graphql/schema/union.rb +1 -1
  29. data/lib/graphql/schema/warden.rb +73 -57
  30. data/lib/graphql/schema.rb +154 -43
  31. data/lib/graphql/subscriptions/event.rb +1 -1
  32. data/lib/graphql/subscriptions.rb +2 -2
  33. data/lib/graphql/version.rb +1 -1
  34. metadata +17 -17
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9997a74f471a1d4abbca1fe24f276db80bbad6f87b278a8083491ced64354906
4
- data.tar.gz: 4265a7d1e76f784ad3df4739328fedfae1698e26dc4a1fee982971650e553c47
3
+ metadata.gz: ab900a93fdb2a8326a75035bf03e27e4874cc959c916d9ac8f85602c78f55c53
4
+ data.tar.gz: ca2c67da2b27b4a661759f5e00b49178d12ae312f77d536959e7d825ecd516af
5
5
  SHA512:
6
- metadata.gz: 2b1c5bed0660b9c194814e889384b3a37a27967c1f808aff55e693431056729434a2556cf85e9dd4c184262f8ad1344fe86316c3ffa12afc2c87315d69773f04
7
- data.tar.gz: 0b2c961d00651a479ce183e0c77d23d2090df85f46490aac53d724d0f657f9cfe1c49bda191feec4b3a90f1cf671cd55f0a6a97d0c79c20d567fc47e214f921d
6
+ metadata.gz: 47639dd694f5c027bd05a2b4fae198fad2b98154ba024785f6fcd844917be5b9bf841ed82b1753061ba9890c5881a8082a62e619090492c7247b345163f45322
7
+ data.tar.gz: df76cec39f5bba1ab58958595d508869fd70610feea72af54ff40c0cd4649b8a9bc6ad6d891907b9b69fc517602bf50057d86dab563223988fb5f4ccd2763c0b
@@ -28,17 +28,22 @@ module GraphQL
28
28
  def initialize(query)
29
29
  @max_depth = 0
30
30
  @current_depth = 0
31
+ @count_introspection_fields = query.schema.count_introspection_fields
31
32
  super
32
33
  end
33
34
 
34
35
  def on_enter_field(node, parent, visitor)
35
- return if visitor.skipping? || visitor.visiting_fragment_definition?
36
+ return if visitor.skipping? ||
37
+ visitor.visiting_fragment_definition? ||
38
+ (@count_introspection_fields == false && visitor.field_definition.introspection?)
36
39
 
37
40
  @current_depth += 1
38
41
  end
39
42
 
40
43
  def on_leave_field(node, parent, visitor)
41
- return if visitor.skipping? || visitor.visiting_fragment_definition?
44
+ return if visitor.skipping? ||
45
+ visitor.visiting_fragment_definition? ||
46
+ (@count_introspection_fields == false && visitor.field_definition.introspection?)
42
47
 
43
48
  if @max_depth < @current_depth
44
49
  @max_depth = @current_depth
@@ -61,6 +61,32 @@ module GraphQL
61
61
  @nonblocking
62
62
  end
63
63
 
64
+ # This is called before the fiber is spawned, from the parent context (i.e. from
65
+ # the thread or fiber that it is scheduled from).
66
+ #
67
+ # @return [Hash<Symbol, Object>] Current fiber-local variables
68
+ def get_fiber_variables
69
+ fiber_vars = {}
70
+ Thread.current.keys.each do |fiber_var_key|
71
+ # This variable should be fresh in each new fiber
72
+ if fiber_var_key != :__graphql_runtime_info
73
+ fiber_vars[fiber_var_key] = Thread.current[fiber_var_key]
74
+ end
75
+ end
76
+ fiber_vars
77
+ end
78
+
79
+ # Set up the fiber variables in a new fiber.
80
+ #
81
+ # This is called within the fiber, right after it is spawned.
82
+ #
83
+ # @param vars [Hash<Symbol, Object>] Fiber-local variables from {get_fiber_variables}
84
+ # @return [void]
85
+ def set_fiber_variables(vars)
86
+ vars.each { |k, v| Thread.current[k] = v }
87
+ nil
88
+ end
89
+
64
90
  # Get a Source instance from this dataloader, for calling `.load(...)` or `.request(...)` on.
65
91
  #
66
92
  # @param source_class [Class<GraphQL::Dataloader::Source]
@@ -295,23 +321,16 @@ module GraphQL
295
321
  #
296
322
  # @see https://github.com/rmosolgo/graphql-ruby/issues/3449
297
323
  def spawn_fiber
298
- fiber_locals = {}
299
-
300
- Thread.current.keys.each do |fiber_var_key|
301
- # This variable should be fresh in each new fiber
302
- if fiber_var_key != :__graphql_runtime_info
303
- fiber_locals[fiber_var_key] = Thread.current[fiber_var_key]
304
- end
305
- end
324
+ fiber_vars = get_fiber_variables
306
325
 
307
326
  if @nonblocking
308
327
  Fiber.new(blocking: false) do
309
- fiber_locals.each { |k, v| Thread.current[k] = v }
328
+ set_fiber_variables(fiber_vars)
310
329
  yield
311
330
  end
312
331
  else
313
332
  Fiber.new do
314
- fiber_locals.each { |k, v| Thread.current[k] = v }
333
+ set_fiber_variables(fiber_vars)
315
334
  yield
316
335
  end
317
336
  end
@@ -107,7 +107,7 @@ module GraphQL
107
107
  when GraphQLResultArray
108
108
  # There's no special handling of arrays because currently, there's no way to split the execution
109
109
  # of a list over several concurrent flows.
110
- next_result.set_child_result(key, value)
110
+ into_result.set_child_result(key, value)
111
111
  else
112
112
  # We have to assume that, since this passed the `fields_will_merge` selection,
113
113
  # that the old and new values are the same.
@@ -52,13 +52,6 @@ module GraphQL
52
52
  # { Class => Boolean }
53
53
  @lazy_cache = {}
54
54
  @lazy_cache.compare_by_identity
55
-
56
- @gathered_selections_cache = Hash.new { |h, k|
57
- cache = {}
58
- cache.compare_by_identity
59
- h[k] = cache
60
- }
61
- @gathered_selections_cache.compare_by_identity
62
55
  end
63
56
 
64
57
  def final_result
@@ -98,7 +91,7 @@ module GraphQL
98
91
  @response = nil
99
92
  else
100
93
  call_method_on_directives(:resolve, runtime_object, root_operation.directives) do # execute query level directives
101
- gathered_selections = gather_selections(runtime_object, root_type, nil, root_operation.selections)
94
+ gathered_selections = gather_selections(runtime_object, root_type, root_operation.selections)
102
95
  # This is kind of a hack -- `gathered_selections` is an Array if any of the selections
103
96
  # require isolation during execution (because of runtime directives). In that case,
104
97
  # make a new, isolated result hash for writing the result into. (That isolated response
@@ -143,18 +136,11 @@ module GraphQL
143
136
  nil
144
137
  end
145
138
 
146
- def gather_selections(owner_object, owner_type, ast_node_for_caching, selections, selections_to_run = nil, selections_by_name = nil)
147
- if ast_node_for_caching && (cached_selections = @gathered_selections_cache[ast_node_for_caching][owner_type])
148
- return cached_selections
149
- end
150
- selections_by_name ||= {} # allocate this default here so we check the cache first
151
-
152
- should_cache = true
139
+ def gather_selections(owner_object, owner_type, selections, selections_to_run = nil, selections_by_name = {})
153
140
 
154
141
  selections.each do |node|
155
142
  # Skip gathering this if the directive says so
156
143
  if !directives_include?(node, owner_object, owner_type)
157
- should_cache = false
158
144
  next
159
145
  end
160
146
 
@@ -179,7 +165,6 @@ module GraphQL
179
165
  if @runtime_directive_names.any? && node.directives.any? { |d| @runtime_directive_names.include?(d.name) }
180
166
  next_selections = {}
181
167
  next_selections[:graphql_directives] = node.directives
182
- should_cache = false
183
168
  if selections_to_run
184
169
  selections_to_run << next_selections
185
170
  else
@@ -197,28 +182,24 @@ module GraphQL
197
182
  type_defn = schema.get_type(node.type.name, context)
198
183
 
199
184
  if query.warden.possible_types(type_defn).include?(owner_type)
200
- gather_selections(owner_object, owner_type, nil, node.selections, selections_to_run, next_selections)
185
+ gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections)
201
186
  end
202
187
  else
203
188
  # it's an untyped fragment, definitely continue
204
- gather_selections(owner_object, owner_type, nil, node.selections, selections_to_run, next_selections)
189
+ gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections)
205
190
  end
206
191
  when GraphQL::Language::Nodes::FragmentSpread
207
192
  fragment_def = query.fragments[node.name]
208
193
  type_defn = query.get_type(fragment_def.type.name)
209
194
  if query.warden.possible_types(type_defn).include?(owner_type)
210
- gather_selections(owner_object, owner_type, nil, fragment_def.selections, selections_to_run, next_selections)
195
+ gather_selections(owner_object, owner_type, fragment_def.selections, selections_to_run, next_selections)
211
196
  end
212
197
  else
213
198
  raise "Invariant: unexpected selection class: #{node.class}"
214
199
  end
215
200
  end
216
201
  end
217
- result = selections_to_run || selections_by_name
218
- if should_cache
219
- @gathered_selections_cache[ast_node_for_caching][owner_type] = result
220
- end
221
- result
202
+ selections_to_run || selections_by_name
222
203
  end
223
204
 
224
205
  NO_ARGS = GraphQL::EmptyObjects::EMPTY_HASH
@@ -619,7 +600,7 @@ module GraphQL
619
600
  response_hash = GraphQLResultHash.new(result_name, selection_result, is_non_null)
620
601
  set_result(selection_result, result_name, response_hash, true, is_non_null)
621
602
 
622
- gathered_selections = gather_selections(continue_value, current_type, ast_node, next_selections)
603
+ gathered_selections = gather_selections(continue_value, current_type, next_selections)
623
604
  # There are two possibilities for `gathered_selections`:
624
605
  # 1. All selections of this object should be evaluated together (there are no runtime directives modifying execution).
625
606
  # This case is handled below, and the result can be written right into the main `response_hash` above.
@@ -80,6 +80,22 @@ module GraphQL
80
80
  selection(field_name, selected_type: selected_type, arguments: arguments).selected?
81
81
  end
82
82
 
83
+ # True if this node has a selection with alias matching `alias_name`.
84
+ # If `alias_name` is a String, it is treated as a GraphQL-style (camelized)
85
+ # field name and used verbatim. If `alias_name` is a Symbol, it is
86
+ # treated as a Ruby-style (underscored) name and camelized before comparing.
87
+ #
88
+ # If `arguments:` is provided, each provided key/value will be matched
89
+ # against the arguments in the next selection. This method will return false
90
+ # if any of the given `arguments:` are not present and matching in the next selection.
91
+ # (But, the next selection may contain _more_ than the given arguments.)
92
+ # @param alias_name [String, Symbol]
93
+ # @param arguments [Hash] Arguments which must match in the selection
94
+ # @return [Boolean]
95
+ def selects_alias?(alias_name, arguments: nil)
96
+ alias_selection(alias_name, arguments: arguments).selected?
97
+ end
98
+
83
99
  # @return [Boolean] True if this lookahead represents a field that was requested
84
100
  def selected?
85
101
  true
@@ -105,6 +121,7 @@ module GraphQL
105
121
  .tap(&:flatten!)
106
122
  end
107
123
 
124
+
108
125
  if (match_by_orig_name = all_fields.find { |f| f.original_name == field_name })
109
126
  match_by_orig_name
110
127
  else
@@ -114,23 +131,29 @@ module GraphQL
114
131
  @query.get_field(selected_type, guessed_name)
115
132
  end
116
133
  end
134
+ lookahead_for_selection(next_field_defn, selected_type, arguments)
135
+ end
117
136
 
118
- if next_field_defn
119
- next_nodes = []
120
- @ast_nodes.each do |ast_node|
121
- ast_node.selections.each do |selection|
122
- find_selected_nodes(selection, next_field_defn, arguments: arguments, matches: next_nodes)
123
- end
124
- end
137
+ # Like {#selection}, but for aliases.
138
+ # It returns a null object (check with {#selected?})
139
+ # @return [GraphQL::Execution::Lookahead]
140
+ def alias_selection(alias_name, selected_type: @selected_type, arguments: nil)
141
+ alias_cache_key = [alias_name, arguments]
142
+ return alias_selections[key] if alias_selections.key?(alias_name)
125
143
 
126
- if next_nodes.any?
127
- Lookahead.new(query: @query, ast_nodes: next_nodes, field: next_field_defn, owner_type: selected_type)
128
- else
129
- NULL_LOOKAHEAD
130
- end
131
- else
132
- NULL_LOOKAHEAD
144
+ alias_node = lookup_alias_node(ast_nodes, alias_name)
145
+ return NULL_LOOKAHEAD unless alias_node
146
+
147
+ next_field_defn = @query.get_field(selected_type, alias_node.name)
148
+
149
+ alias_arguments = @query.arguments_for(alias_node, next_field_defn)
150
+ if alias_arguments.is_a?(::GraphQL::Execution::Interpreter::Arguments)
151
+ alias_arguments = alias_arguments.keyword_arguments
133
152
  end
153
+
154
+ return NULL_LOOKAHEAD if arguments && arguments != alias_arguments
155
+
156
+ alias_selections[alias_cache_key] = lookahead_for_selection(next_field_defn, selected_type, alias_arguments, alias_name)
134
157
  end
135
158
 
136
159
  # Like {#selection}, but for all nodes.
@@ -258,7 +281,7 @@ module GraphQL
258
281
  end
259
282
  find_selections(subselections_by_type, subselections_on_type, on_type, ast_selection.selections, arguments)
260
283
  when GraphQL::Language::Nodes::FragmentSpread
261
- frag_defn = @query.fragments[ast_selection.name] || raise("Invariant: Can't look ahead to nonexistent fragment #{ast_selection.name} (found: #{@query.fragments.keys})")
284
+ frag_defn = lookup_fragment(ast_selection)
262
285
  # Again, assuming a valid AST
263
286
  on_type = @query.get_type(frag_defn.type.name)
264
287
  subselections_on_type = subselections_by_type[on_type] ||= {}
@@ -271,11 +294,11 @@ module GraphQL
271
294
 
272
295
  # If a selection on `node` matches `field_name` (which is backed by `field_defn`)
273
296
  # and matches the `arguments:` constraints, then add that node to `matches`
274
- def find_selected_nodes(node, field_defn, arguments:, matches:)
297
+ def find_selected_nodes(node, field_name, field_defn, arguments:, matches:, alias_name: NOT_CONFIGURED)
275
298
  return if skipped_by_directive?(node)
276
299
  case node
277
300
  when GraphQL::Language::Nodes::Field
278
- if node.name == field_defn.graphql_name
301
+ if node.name == field_name && (NOT_CONFIGURED.equal?(alias_name) || node.alias == alias_name)
279
302
  if arguments.nil? || arguments.empty?
280
303
  # No constraint applied
281
304
  matches << node
@@ -284,10 +307,10 @@ module GraphQL
284
307
  end
285
308
  end
286
309
  when GraphQL::Language::Nodes::InlineFragment
287
- node.selections.each { |s| find_selected_nodes(s, field_defn, arguments: arguments, matches: matches) }
310
+ node.selections.each { |s| find_selected_nodes(s, field_name, field_defn, arguments: arguments, matches: matches, alias_name: alias_name) }
288
311
  when GraphQL::Language::Nodes::FragmentSpread
289
- frag_defn = @query.fragments[node.name] || raise("Invariant: Can't look ahead to nonexistent fragment #{node.name} (found: #{@query.fragments.keys})")
290
- frag_defn.selections.each { |s| find_selected_nodes(s, field_defn, arguments: arguments, matches: matches) }
312
+ frag_defn = lookup_fragment(node)
313
+ frag_defn.selections.each { |s| find_selected_nodes(s, field_name, field_defn, arguments: arguments, matches: matches, alias_name: alias_name) }
291
314
  else
292
315
  raise "Unexpected selection comparison on #{node.class.name} (#{node})"
293
316
  end
@@ -306,6 +329,50 @@ module GraphQL
306
329
  query_kwargs.key?(arg_name_sym) && query_kwargs[arg_name_sym] == arg_value
307
330
  end
308
331
  end
332
+
333
+ def lookahead_for_selection(field_defn, selected_type, arguments, alias_name = NOT_CONFIGURED)
334
+ return NULL_LOOKAHEAD unless field_defn
335
+
336
+ next_nodes = []
337
+ field_name = field_defn.name
338
+ @ast_nodes.each do |ast_node|
339
+ ast_node.selections.each do |selection|
340
+ find_selected_nodes(selection, field_name, field_defn, arguments: arguments, matches: next_nodes, alias_name: alias_name)
341
+ end
342
+ end
343
+
344
+ return NULL_LOOKAHEAD if next_nodes.empty?
345
+
346
+ Lookahead.new(query: @query, ast_nodes: next_nodes, field: field_defn, owner_type: selected_type)
347
+ end
348
+
349
+ def alias_selections
350
+ return @alias_selections if defined?(@alias_selections)
351
+ @alias_selections ||= {}
352
+ end
353
+
354
+ def lookup_alias_node(nodes, name)
355
+ return if nodes.empty?
356
+
357
+ nodes.flat_map(&:children)
358
+ .flat_map { |child| unwrap_fragments(child) }
359
+ .find { |child| child.is_a?(GraphQL::Language::Nodes::Field) && child.alias == name }
360
+ end
361
+
362
+ def unwrap_fragments(node)
363
+ case node
364
+ when GraphQL::Language::Nodes::InlineFragment
365
+ node.children
366
+ when GraphQL::Language::Nodes::FragmentSpread
367
+ lookup_fragment(node).children
368
+ else
369
+ [node]
370
+ end
371
+ end
372
+
373
+ def lookup_fragment(ast_selection)
374
+ @query.fragments[ast_selection.name] || raise("Invariant: Can't look ahead to nonexistent fragment #{ast_selection.name} (found: #{@query.fragments.keys})")
375
+ end
309
376
  end
310
377
  end
311
378
  end
@@ -12,10 +12,14 @@ module GraphQL
12
12
  attr_reader :id
13
13
  # @return [Object] The value found with this ID
14
14
  attr_reader :object
15
- def initialize(argument:, id:, object:)
15
+ # @return [GraphQL::Query::Context]
16
+ attr_reader :context
17
+
18
+ def initialize(argument:, id:, object:, context:)
16
19
  @id = id
17
20
  @argument = argument
18
21
  @object = object
22
+ @context = context
19
23
  super("No object found for `#{argument.graphql_name}: #{id.inspect}`")
20
24
  end
21
25
  end
@@ -22,10 +22,11 @@ module GraphQL
22
22
  attr_reader :context
23
23
 
24
24
  def context=(new_ctx)
25
- current_runtime_state = Thread.current[:__graphql_runtime_info]
26
- query_runtime_state = current_runtime_state[new_ctx.query]
27
- @was_authorized_by_scope_items = query_runtime_state.was_authorized_by_scope_items
28
25
  @context = new_ctx
26
+ if @was_authorized_by_scope_items.nil?
27
+ @was_authorized_by_scope_items = detect_was_authorized_by_scope_items
28
+ end
29
+ @context
29
30
  end
30
31
 
31
32
  # @return [Object] the object this collection belongs to
@@ -90,13 +91,7 @@ module GraphQL
90
91
  else
91
92
  default_page_size
92
93
  end
93
- @was_authorized_by_scope_items = if @context
94
- current_runtime_state = Thread.current[:__graphql_runtime_info]
95
- query_runtime_state = current_runtime_state[@context.query]
96
- query_runtime_state.was_authorized_by_scope_items
97
- else
98
- nil
99
- end
94
+ @was_authorized_by_scope_items = detect_was_authorized_by_scope_items
100
95
  end
101
96
 
102
97
  def was_authorized_by_scope_items?
@@ -226,6 +221,16 @@ module GraphQL
226
221
 
227
222
  private
228
223
 
224
+ def detect_was_authorized_by_scope_items
225
+ if @context &&
226
+ (current_runtime_state = Thread.current[:__graphql_runtime_info]) &&
227
+ (query_runtime_state = current_runtime_state[@context.query])
228
+ query_runtime_state.was_authorized_by_scope_items
229
+ else
230
+ nil
231
+ end
232
+ end
233
+
229
234
  # @param argument [nil, Integer] `first` or `last`, as provided by the client
230
235
  # @param max_page_size [nil, Integer]
231
236
  # @return [nil, Integer] `nil` if the input was `nil`, otherwise a value between `0` and `max_page_size`
@@ -13,8 +13,7 @@ module GraphQL
13
13
  end
14
14
 
15
15
  def relation_count(relation)
16
- # Mongo's `.count` doesn't apply limit or skip, which we need. So we have to load _everything_!
17
- relation.to_a.count
16
+ relation.all.count(relation.options.slice(:limit, :skip))
18
17
  end
19
18
 
20
19
  def null_relation(relation)
@@ -237,6 +237,10 @@ module GraphQL
237
237
  @storage.key?(ns)
238
238
  end
239
239
 
240
+ def logger
241
+ @query && @query.logger
242
+ end
243
+
240
244
  def inspect
241
245
  "#<Query::Context ...>"
242
246
  end
@@ -3,6 +3,8 @@ module GraphQL
3
3
  class Query
4
4
  # This object can be `ctx` in places where there is no query
5
5
  class NullContext
6
+ include Singleton
7
+
6
8
  class NullQuery
7
9
  def after_lazy(value)
8
10
  yield(value)
@@ -27,16 +29,6 @@ module GraphQL
27
29
  def interpreter?
28
30
  true
29
31
  end
30
-
31
- class << self
32
- extend Forwardable
33
-
34
- def instance
35
- @instance ||= self.new
36
- end
37
-
38
- def_delegators :instance, :query, :warden, :schema, :interpreter?, :dataloader, :[], :fetch, :dig, :key?
39
- end
40
32
  end
41
33
  end
42
34
  end
data/lib/graphql/query.rb CHANGED
@@ -162,6 +162,14 @@ module GraphQL
162
162
 
163
163
  @result_values = nil
164
164
  @executed = false
165
+
166
+ @logger = if context && context[:logger] == false
167
+ Logger.new(IO::NULL)
168
+ elsif context && (l = context[:logger])
169
+ l
170
+ else
171
+ schema.default_logger
172
+ end
165
173
  end
166
174
 
167
175
  # If a document was provided to `GraphQL::Schema#execute` instead of the raw query string, we will need to get it from the document
@@ -369,6 +377,8 @@ module GraphQL
369
377
  end
370
378
  end
371
379
 
380
+ attr_reader :logger
381
+
372
382
  private
373
383
 
374
384
  def find_operation(operations, operation_name)
@@ -432,14 +432,12 @@ module GraphQL
432
432
  builder = self
433
433
 
434
434
  field_definitions.each do |field_definition|
435
- type_name = resolve_type_name(field_definition.type)
436
435
  resolve_method_name = -"resolve_field_#{field_definition.name}"
437
436
  schema_field_defn = owner.field(
438
437
  field_definition.name,
439
438
  description: field_definition.description,
440
439
  type: type_resolver.call(field_definition.type),
441
440
  null: true,
442
- connection: type_name.end_with?("Connection"),
443
441
  connection_extension: nil,
444
442
  deprecation_reason: build_deprecation_reason(field_definition.directives),
445
443
  ast_node: field_definition,
@@ -487,15 +485,6 @@ module GraphQL
487
485
  }
488
486
  resolve_type_proc
489
487
  end
490
-
491
- def resolve_type_name(type)
492
- case type
493
- when GraphQL::Language::Nodes::TypeName
494
- return type.name
495
- else
496
- resolve_type_name(type.of_type)
497
- end
498
- end
499
488
  end
500
489
 
501
490
  private_constant :Builder
@@ -6,6 +6,18 @@ module GraphQL
6
6
  description "Requires that exactly one field must be supplied and that field must not be `null`."
7
7
  locations(GraphQL::Schema::Directive::INPUT_OBJECT)
8
8
  default_directive true
9
+
10
+ def initialize(...)
11
+ super
12
+
13
+ owner.extend(IsOneOf)
14
+ end
15
+
16
+ module IsOneOf
17
+ def one_of?
18
+ true
19
+ end
20
+ end
9
21
  end
10
22
  end
11
23
  end
@@ -119,7 +119,7 @@ module GraphQL
119
119
  # - lazy resolution
120
120
  # Probably, those won't be needed here, since these are configuration arguments,
121
121
  # not runtime arguments.
122
- @arguments = self.class.coerce_arguments(nil, arguments, Query::NullContext)
122
+ @arguments = self.class.coerce_arguments(nil, arguments, Query::NullContext.instance)
123
123
  end
124
124
 
125
125
  def graphql_name
@@ -68,7 +68,7 @@ module GraphQL
68
68
  end
69
69
 
70
70
  # @return [Array<GraphQL::Schema::EnumValue>] Possible values of this enum
71
- def enum_values(context = GraphQL::Query::NullContext)
71
+ def enum_values(context = GraphQL::Query::NullContext.instance)
72
72
  inherited_values = superclass.respond_to?(:enum_values) ? superclass.enum_values(context) : nil
73
73
  visible_values = []
74
74
  warden = Warden.from_context(context)
@@ -110,7 +110,7 @@ module GraphQL
110
110
  end
111
111
 
112
112
  # @return [Hash<String => GraphQL::Schema::EnumValue>] Possible values of this enum, keyed by name.
113
- def values(context = GraphQL::Query::NullContext)
113
+ def values(context = GraphQL::Query::NullContext.instance)
114
114
  enum_values(context).each_with_object({}) { |val, obj| obj[val.graphql_name] = val }
115
115
  end
116
116
 
@@ -12,9 +12,10 @@ module GraphQL
12
12
  if ret_type.respond_to?(:scope_items)
13
13
  scoped_items = ret_type.scope_items(value, context)
14
14
  if !scoped_items.equal?(value) && !ret_type.reauthorize_scoped_objects
15
- current_runtime_state = Thread.current[:__graphql_runtime_info]
16
- query_runtime_state = current_runtime_state[context.query]
17
- query_runtime_state.was_authorized_by_scope_items = true
15
+ if (current_runtime_state = Thread.current[:__graphql_runtime_info]) &&
16
+ (query_runtime_state = current_runtime_state[context.query])
17
+ query_runtime_state.was_authorized_by_scope_items = true
18
+ end
18
19
  end
19
20
  scoped_items
20
21
  else
@@ -138,7 +138,7 @@ module GraphQL
138
138
  # As a last ditch, try to force loading the return type:
139
139
  type.unwrap.name
140
140
  end
141
- @connection = return_type_name.end_with?("Connection")
141
+ @connection = return_type_name.end_with?("Connection") && return_type_name != "Connection"
142
142
  else
143
143
  @connection
144
144
  end
@@ -704,7 +704,7 @@ module GraphQL
704
704
  inner_object.dig(*@dig_keys)
705
705
  elsif inner_object.key?(@method_sym)
706
706
  inner_object[@method_sym]
707
- elsif inner_object.key?(@method_str)
707
+ elsif inner_object.key?(@method_str) || !inner_object.default_proc.nil?
708
708
  inner_object[@method_str]
709
709
  elsif @fallback_value != NOT_CONFIGURED
710
710
  @fallback_value
@@ -54,11 +54,11 @@ module GraphQL
54
54
  end
55
55
  end
56
56
 
57
- def field_arguments(context = GraphQL::Query::NullContext)
57
+ def field_arguments(context = GraphQL::Query::NullContext.instance)
58
58
  dummy.arguments(context)
59
59
  end
60
60
 
61
- def get_field_argument(name, context = GraphQL::Query::NullContext)
61
+ def get_field_argument(name, context = GraphQL::Query::NullContext.instance)
62
62
  dummy.get_argument(name, context)
63
63
  end
64
64
 
@@ -79,7 +79,7 @@ module GraphQL
79
79
  end
80
80
 
81
81
  def self.one_of?
82
- directives.any? { |d| d.is_a?(GraphQL::Schema::Directive::OneOf) }
82
+ false # Re-defined when `OneOf` is added
83
83
  end
84
84
 
85
85
  def unwrap_value(value)