graphql 2.1.1 → 2.1.5

Sign up to get free protection for your applications and to get access to all the features.
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)