graphql 2.0.20 → 2.0.21

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 (42) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql/backtrace/trace.rb +96 -0
  3. data/lib/graphql/backtrace.rb +6 -1
  4. data/lib/graphql/execution/interpreter/arguments.rb +1 -1
  5. data/lib/graphql/execution/interpreter/arguments_cache.rb +2 -3
  6. data/lib/graphql/execution/interpreter/runtime.rb +200 -161
  7. data/lib/graphql/execution/interpreter.rb +1 -1
  8. data/lib/graphql/filter.rb +7 -2
  9. data/lib/graphql/language/document_from_schema_definition.rb +25 -9
  10. data/lib/graphql/language/nodes.rb +2 -2
  11. data/lib/graphql/language/parser.rb +475 -458
  12. data/lib/graphql/language/parser.y +5 -1
  13. data/lib/graphql/pagination/connection.rb +5 -5
  14. data/lib/graphql/query/context.rb +13 -12
  15. data/lib/graphql/query/null_context.rb +1 -1
  16. data/lib/graphql/query.rb +9 -5
  17. data/lib/graphql/schema/argument.rb +7 -9
  18. data/lib/graphql/schema/build_from_definition.rb +15 -3
  19. data/lib/graphql/schema/enum_value.rb +2 -5
  20. data/lib/graphql/schema/field.rb +14 -13
  21. data/lib/graphql/schema/field_extension.rb +1 -4
  22. data/lib/graphql/schema/find_inherited_value.rb +2 -7
  23. data/lib/graphql/schema/member/has_arguments.rb +1 -1
  24. data/lib/graphql/schema/member/has_directives.rb +4 -6
  25. data/lib/graphql/schema/member/has_fields.rb +80 -36
  26. data/lib/graphql/schema/member/has_validators.rb +2 -2
  27. data/lib/graphql/schema/resolver.rb +4 -4
  28. data/lib/graphql/schema/validator.rb +1 -1
  29. data/lib/graphql/schema/warden.rb +3 -1
  30. data/lib/graphql/schema.rb +40 -11
  31. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +12 -4
  32. data/lib/graphql/static_validation/rules/fields_will_merge.rb +2 -2
  33. data/lib/graphql/tracing/appsignal_trace.rb +6 -0
  34. data/lib/graphql/tracing/legacy_trace.rb +65 -0
  35. data/lib/graphql/tracing/notifications_trace.rb +2 -1
  36. data/lib/graphql/tracing/platform_trace.rb +21 -19
  37. data/lib/graphql/tracing/prometheus_tracing/graphql_collector.rb +1 -1
  38. data/lib/graphql/tracing/trace.rb +75 -0
  39. data/lib/graphql/tracing.rb +3 -123
  40. data/lib/graphql/version.rb +1 -1
  41. data/lib/graphql.rb +4 -0
  42. metadata +5 -2
@@ -8,6 +8,18 @@ module GraphQL
8
8
  #
9
9
  # @api private
10
10
  class Runtime
11
+ class CurrentState
12
+ def initialize
13
+ @current_object = nil
14
+ @current_field = nil
15
+ @current_arguments = nil
16
+ @current_result_name = nil
17
+ @current_result = nil
18
+ end
19
+
20
+ attr_accessor :current_result, :current_result_name,
21
+ :current_arguments, :current_field, :current_object
22
+ end
11
23
 
12
24
  module GraphQLResult
13
25
  def initialize(result_name, parent_result)
@@ -54,7 +66,7 @@ module GraphQL
54
66
 
55
67
  attr_accessor :graphql_merged_into
56
68
 
57
- def []=(key, value)
69
+ def set_leaf(key, value)
58
70
  # This is a hack.
59
71
  # Basically, this object is merged into the root-level result at some point.
60
72
  # But the problem is, some lazies are created whose closures retain reference to _this_
@@ -64,20 +76,24 @@ module GraphQL
64
76
  # In order to return a proper partial result (eg, for a directive), we have to update this object, too.
65
77
  # Yowza.
66
78
  if (t = @graphql_merged_into)
67
- t[key] = value
79
+ t.set_leaf(key, value)
68
80
  end
69
81
 
70
- if value.respond_to?(:graphql_result_data)
71
- @graphql_result_data[key] = value.graphql_result_data
72
- # If we encounter some part of this response that requires metadata tracking,
73
- # then create the metadata hash if necessary. It will be kept up-to-date after this.
74
- (@graphql_metadata ||= @graphql_result_data.dup)[key] = value
75
- else
76
- @graphql_result_data[key] = value
77
- # keep this up-to-date if it's been initialized
78
- @graphql_metadata && @graphql_metadata[key] = value
79
- end
82
+ @graphql_result_data[key] = value
83
+ # keep this up-to-date if it's been initialized
84
+ @graphql_metadata && @graphql_metadata[key] = value
85
+
86
+ value
87
+ end
80
88
 
89
+ def set_child_result(key, value)
90
+ if (t = @graphql_merged_into)
91
+ t.set_child_result(key, value)
92
+ end
93
+ @graphql_result_data[key] = value.graphql_result_data
94
+ # If we encounter some part of this response that requires metadata tracking,
95
+ # then create the metadata hash if necessary. It will be kept up-to-date after this.
96
+ (@graphql_metadata ||= @graphql_result_data.dup)[key] = value
81
97
  value
82
98
  end
83
99
 
@@ -101,6 +117,29 @@ module GraphQL
101
117
  def [](k)
102
118
  (@graphql_metadata || @graphql_result_data)[k]
103
119
  end
120
+
121
+ def merge_into(into_result)
122
+ self.each do |key, value|
123
+ case value
124
+ when GraphQLResultHash
125
+ next_into = into_result[key]
126
+ if next_into
127
+ value.merge_into(next_into)
128
+ else
129
+ into_result.set_child_result(key, value)
130
+ end
131
+ when GraphQLResultArray
132
+ # There's no special handling of arrays because currently, there's no way to split the execution
133
+ # of a list over several concurrent flows.
134
+ next_result.set_child_result(key, value)
135
+ else
136
+ # We have to assume that, since this passed the `fields_will_merge` selection,
137
+ # that the old and new values are the same.
138
+ into_result.set_leaf(key, value)
139
+ end
140
+ end
141
+ @graphql_merged_into = into_result
142
+ end
104
143
  end
105
144
 
106
145
  class GraphQLResultArray
@@ -123,19 +162,25 @@ module GraphQL
123
162
  @graphql_result_data.delete_at(delete_at_index)
124
163
  end
125
164
 
126
- def []=(idx, value)
165
+ def set_leaf(idx, value)
127
166
  if @skip_indices
128
167
  offset_by = @skip_indices.count { |skipped_idx| skipped_idx < idx }
129
168
  idx -= offset_by
130
169
  end
131
- if value.respond_to?(:graphql_result_data)
132
- @graphql_result_data[idx] = value.graphql_result_data
133
- (@graphql_metadata ||= @graphql_result_data.dup)[idx] = value
134
- else
135
- @graphql_result_data[idx] = value
136
- @graphql_metadata && @graphql_metadata[idx] = value
137
- end
170
+ @graphql_result_data[idx] = value
171
+ @graphql_metadata && @graphql_metadata[idx] = value
172
+ value
173
+ end
138
174
 
175
+ def set_child_result(idx, value)
176
+ if @skip_indices
177
+ offset_by = @skip_indices.count { |skipped_idx| skipped_idx < idx }
178
+ idx -= offset_by
179
+ end
180
+ @graphql_result_data[idx] = value.graphql_result_data
181
+ # If we encounter some part of this response that requires metadata tracking,
182
+ # then create the metadata hash if necessary. It will be kept up-to-date after this.
183
+ (@graphql_metadata ||= @graphql_result_data.dup)[idx] = value
139
184
  value
140
185
  end
141
186
 
@@ -157,15 +202,6 @@ module GraphQL
157
202
  # @return [GraphQL::Query::Context]
158
203
  attr_reader :context
159
204
 
160
- def thread_info
161
- info = Thread.current[:__graphql_runtime_info]
162
- if !info
163
- new_ti = {}
164
- info = Thread.current[:__graphql_runtime_info] = new_ti
165
- end
166
- info
167
- end
168
-
169
205
  def initialize(query:, lazies_at_depth:)
170
206
  @query = query
171
207
  @dataloader = query.multiplex.dataloader
@@ -218,7 +254,9 @@ module GraphQL
218
254
  root_operation = query.selected_operation
219
255
  root_op_type = root_operation.operation_type || "query"
220
256
  root_type = schema.root_type_for_operation(root_op_type)
221
- set_all_interpreter_context(query.root_value, nil, nil, nil, @response)
257
+ st = get_current_runtime_state
258
+ st.current_object = query.root_value
259
+ st.current_result = @response
222
260
  object_proxy = authorized_new(root_type, query.root_value, context)
223
261
  object_proxy = schema.sync_lazy(object_proxy)
224
262
 
@@ -245,7 +283,10 @@ module GraphQL
245
283
  end
246
284
 
247
285
  @dataloader.append_job {
248
- set_all_interpreter_context(query.root_value, nil, nil, nil, selection_response)
286
+ st = get_current_runtime_state
287
+ st.current_object = query.root_value
288
+ st.current_result = selection_response
289
+
249
290
  call_method_on_directives(:resolve, object_proxy, selections.graphql_directives) do
250
291
  evaluate_selections(
251
292
  object_proxy,
@@ -265,28 +306,6 @@ module GraphQL
265
306
  nil
266
307
  end
267
308
 
268
- # @return [void]
269
- def deep_merge_selection_result(from_result, into_result)
270
- from_result.each do |key, value|
271
- if !into_result.key?(key)
272
- into_result[key] = value
273
- else
274
- case value
275
- when GraphQLResultHash
276
- deep_merge_selection_result(value, into_result[key])
277
- else
278
- # We have to assume that, since this passed the `fields_will_merge` selection,
279
- # that the old and new values are the same.
280
- # There's no special handling of arrays because currently, there's no way to split the execution
281
- # of a list over several concurrent flows.
282
- into_result[key] = value
283
- end
284
- end
285
- end
286
- from_result.graphql_merged_into = into_result
287
- nil
288
- end
289
-
290
309
  def gather_selections(owner_object, owner_type, selections, selections_to_run = nil, selections_by_name = GraphQLSelectionSet.new)
291
310
  selections.each do |node|
292
311
  # Skip gathering this if the directive says so
@@ -360,11 +379,14 @@ module GraphQL
360
379
  selections_to_run || selections_by_name
361
380
  end
362
381
 
363
- NO_ARGS = {}.freeze
382
+ NO_ARGS = GraphQL::EmptyObjects::EMPTY_HASH
364
383
 
365
384
  # @return [void]
366
385
  def evaluate_selections(owner_object, owner_type, is_eager_selection, gathered_selections, selections_result, target_result, parent_object) # rubocop:disable Metrics/ParameterLists
367
- set_all_interpreter_context(owner_object, nil, nil, nil, selections_result)
386
+ st = get_current_runtime_state
387
+ st.current_object = owner_object
388
+ st.current_result_name = nil
389
+ st.current_result = selections_result
368
390
 
369
391
  finished_jobs = 0
370
392
  enqueued_jobs = gathered_selections.size
@@ -375,7 +397,7 @@ module GraphQL
375
397
  )
376
398
  finished_jobs += 1
377
399
  if target_result && finished_jobs == enqueued_jobs
378
- deep_merge_selection_result(selections_result, target_result)
400
+ selections_result.merge_into(target_result)
379
401
  end
380
402
  }
381
403
  end
@@ -418,11 +440,16 @@ module GraphQL
418
440
  # This seems janky, but we need to know
419
441
  # the field's return type at this path in order
420
442
  # to propagate `null`
421
- if return_type.non_null?
443
+ return_type_non_null = return_type.non_null?
444
+ if return_type_non_null
422
445
  (selections_result.graphql_non_null_field_names ||= []).push(result_name)
423
446
  end
424
447
  # Set this before calling `run_with_directives`, so that the directive can have the latest path
425
- set_all_interpreter_context(nil, field_defn, nil, result_name, selections_result)
448
+ st = get_current_runtime_state
449
+ st.current_field = field_defn
450
+ st.current_result = selections_result
451
+ st.current_result_name = result_name
452
+
426
453
  object = owner_object
427
454
 
428
455
  if is_introspection
@@ -432,25 +459,34 @@ module GraphQL
432
459
  total_args_count = field_defn.arguments(context).size
433
460
  if total_args_count == 0
434
461
  resolved_arguments = GraphQL::Execution::Interpreter::Arguments::EMPTY
435
- evaluate_selection_with_args(resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selections_result, parent_object, return_type)
462
+ if field_defn.extras.size == 0
463
+ evaluate_selection_with_resolved_keyword_args(
464
+ NO_ARGS, resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selections_result, parent_object, return_type, return_type_non_null
465
+ )
466
+ else
467
+ evaluate_selection_with_args(resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selections_result, parent_object, return_type, return_type_non_null)
468
+ end
436
469
  else
437
- # TODO remove all arguments(...) usages?
438
470
  @query.arguments_cache.dataload_for(ast_node, field_defn, object) do |resolved_arguments|
439
- evaluate_selection_with_args(resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selections_result, parent_object, return_type)
471
+ evaluate_selection_with_args(resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selections_result, parent_object, return_type, return_type_non_null)
440
472
  end
441
473
  end
442
474
  end
443
475
 
444
- def evaluate_selection_with_args(arguments, field_defn, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selection_result, parent_object, return_type) # rubocop:disable Metrics/ParameterLists
476
+ def evaluate_selection_with_args(arguments, field_defn, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selection_result, parent_object, return_type, return_type_non_null) # rubocop:disable Metrics/ParameterLists
445
477
  after_lazy(arguments, owner: owner_type, field: field_defn, ast_node: ast_node, owner_object: object, arguments: arguments, result_name: result_name, result: selection_result) do |resolved_arguments|
446
478
  if resolved_arguments.is_a?(GraphQL::ExecutionError) || resolved_arguments.is_a?(GraphQL::UnauthorizedError)
447
- continue_value(resolved_arguments, owner_type, field_defn, return_type.non_null?, ast_node, result_name, selection_result)
479
+ continue_value(resolved_arguments, owner_type, field_defn, return_type_non_null, ast_node, result_name, selection_result)
448
480
  next
449
481
  end
450
482
 
451
- kwarg_arguments = if resolved_arguments.empty? && field_defn.extras.empty?
452
- # We can avoid allocating the `{ Symbol => Object }` hash in this case
453
- NO_ARGS
483
+ kwarg_arguments = if field_defn.extras.empty?
484
+ if resolved_arguments.empty?
485
+ # We can avoid allocating the `{ Symbol => Object }` hash in this case
486
+ NO_ARGS
487
+ else
488
+ resolved_arguments.keyword_arguments
489
+ end
454
490
  else
455
491
  # Bundle up the extras, then make a new arguments instance
456
492
  # that includes the extras, too.
@@ -489,61 +525,70 @@ module GraphQL
489
525
  resolved_arguments.keyword_arguments
490
526
  end
491
527
 
492
- set_all_interpreter_context(nil, nil, resolved_arguments, result_name, selection_result)
528
+ evaluate_selection_with_resolved_keyword_args(kwarg_arguments, resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selection_result, parent_object, return_type, return_type_non_null)
529
+ end
530
+ end
493
531
 
494
- # Optimize for the case that field is selected only once
495
- if field_ast_nodes.nil? || field_ast_nodes.size == 1
496
- next_selections = ast_node.selections
497
- directives = ast_node.directives
498
- else
499
- next_selections = []
500
- directives = []
501
- field_ast_nodes.each { |f|
502
- next_selections.concat(f.selections)
503
- directives.concat(f.directives)
504
- }
505
- end
506
-
507
- field_result = call_method_on_directives(:resolve, object, directives) do
508
- # Actually call the field resolver and capture the result
509
- app_result = begin
510
- query.current_trace.execute_field(field: field_defn, ast_node: ast_node, query: query, object: object, arguments: kwarg_arguments) do
511
- field_defn.resolve(object, kwarg_arguments, context)
512
- end
513
- rescue GraphQL::ExecutionError => err
514
- err
515
- rescue StandardError => err
516
- begin
517
- query.handle_or_reraise(err)
518
- rescue GraphQL::ExecutionError => ex_err
519
- ex_err
520
- end
532
+ def evaluate_selection_with_resolved_keyword_args(kwarg_arguments, resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selection_result, parent_object, return_type, return_type_non_null) # rubocop:disable Metrics/ParameterLists
533
+ st = get_current_runtime_state
534
+ st.current_field = field_defn
535
+ st.current_object = object
536
+ st.current_arguments = resolved_arguments
537
+ st.current_result_name = result_name
538
+ st.current_result = selection_result
539
+ # Optimize for the case that field is selected only once
540
+ if field_ast_nodes.nil? || field_ast_nodes.size == 1
541
+ next_selections = ast_node.selections
542
+ directives = ast_node.directives
543
+ else
544
+ next_selections = []
545
+ directives = []
546
+ field_ast_nodes.each { |f|
547
+ next_selections.concat(f.selections)
548
+ directives.concat(f.directives)
549
+ }
550
+ end
551
+
552
+ field_result = call_method_on_directives(:resolve, object, directives) do
553
+ # Actually call the field resolver and capture the result
554
+ app_result = begin
555
+ query.current_trace.execute_field(field: field_defn, ast_node: ast_node, query: query, object: object, arguments: kwarg_arguments) do
556
+ field_defn.resolve(object, kwarg_arguments, context)
521
557
  end
522
- after_lazy(app_result, owner: owner_type, field: field_defn, ast_node: ast_node, owner_object: object, arguments: resolved_arguments, result_name: result_name, result: selection_result) do |inner_result|
523
- continue_value = continue_value(inner_result, owner_type, field_defn, return_type.non_null?, ast_node, result_name, selection_result)
524
- if HALT != continue_value
525
- continue_field(continue_value, owner_type, field_defn, return_type, ast_node, next_selections, false, object, resolved_arguments, result_name, selection_result)
526
- end
558
+ rescue GraphQL::ExecutionError => err
559
+ err
560
+ rescue StandardError => err
561
+ begin
562
+ query.handle_or_reraise(err)
563
+ rescue GraphQL::ExecutionError => ex_err
564
+ ex_err
527
565
  end
528
566
  end
529
-
530
- # If this field is a root mutation field, immediately resolve
531
- # all of its child fields before moving on to the next root mutation field.
532
- # (Subselections of this mutation will still be resolved level-by-level.)
533
- if is_eager_field
534
- Interpreter::Resolve.resolve_all([field_result], @dataloader)
535
- else
536
- # Return this from `after_lazy` because it might be another lazy that needs to be resolved
537
- field_result
567
+ after_lazy(app_result, owner: owner_type, field: field_defn, ast_node: ast_node, owner_object: object, arguments: resolved_arguments, result_name: result_name, result: selection_result) do |inner_result|
568
+ continue_value = continue_value(inner_result, owner_type, field_defn, return_type_non_null, ast_node, result_name, selection_result)
569
+ if HALT != continue_value
570
+ continue_field(continue_value, owner_type, field_defn, return_type, ast_node, next_selections, false, object, resolved_arguments, result_name, selection_result)
571
+ end
538
572
  end
539
573
  end
574
+
575
+ # If this field is a root mutation field, immediately resolve
576
+ # all of its child fields before moving on to the next root mutation field.
577
+ # (Subselections of this mutation will still be resolved level-by-level.)
578
+ if is_eager_field
579
+ Interpreter::Resolve.resolve_all([field_result], @dataloader)
580
+ else
581
+ # Return this from `after_lazy` because it might be another lazy that needs to be resolved
582
+ field_result
583
+ end
540
584
  end
541
585
 
586
+
542
587
  def dead_result?(selection_result)
543
- selection_result.graphql_dead || ((parent = selection_result.graphql_parent) && parent.graphql_dead)
588
+ selection_result.graphql_dead # || ((parent = selection_result.graphql_parent) && parent.graphql_dead)
544
589
  end
545
590
 
546
- def set_result(selection_result, result_name, value)
591
+ def set_result(selection_result, result_name, value, is_child_result)
547
592
  if !dead_result?(selection_result)
548
593
  if value.nil? &&
549
594
  ( # there are two conditions under which `nil` is not allowed in the response:
@@ -563,11 +608,13 @@ module GraphQL
563
608
  if parent.nil? # This is a top-level result hash
564
609
  @response = nil
565
610
  else
566
- set_result(parent, name_in_parent, nil)
611
+ set_result(parent, name_in_parent, nil, false)
567
612
  set_graphql_dead(selection_result)
568
613
  end
614
+ elsif is_child_result
615
+ selection_result.set_child_result(result_name, value)
569
616
  else
570
- selection_result[result_name] = value
617
+ selection_result.set_leaf(result_name, value)
571
618
  end
572
619
  end
573
620
  end
@@ -588,11 +635,10 @@ module GraphQL
588
635
  end
589
636
 
590
637
  def current_path
591
- ti = thread_info
592
- path = ti &&
593
- (result = ti[:current_result]) &&
594
- (result.path)
595
- if path && (rn = ti[:current_result_name])
638
+ st = get_current_runtime_state
639
+ result = st.current_result
640
+ path = result && result.path
641
+ if path && (rn = st.current_result_name)
596
642
  path = path.dup
597
643
  path.push(rn)
598
644
  end
@@ -604,13 +650,13 @@ module GraphQL
604
650
  case value
605
651
  when nil
606
652
  if is_non_null
607
- set_result(selection_result, result_name, nil) do
653
+ set_result(selection_result, result_name, nil, false) do
608
654
  # This block is called if `result_name` is not dead. (Maybe a previous invalid nil caused it be marked dead.)
609
655
  err = parent_type::InvalidNullError.new(parent_type, field, value)
610
656
  schema.type_error(err, context)
611
657
  end
612
658
  else
613
- set_result(selection_result, result_name, nil)
659
+ set_result(selection_result, result_name, nil, false)
614
660
  end
615
661
  HALT
616
662
  when GraphQL::Error
@@ -623,7 +669,7 @@ module GraphQL
623
669
  value.ast_node ||= ast_node
624
670
  context.errors << value
625
671
  if selection_result
626
- set_result(selection_result, result_name, nil)
672
+ set_result(selection_result, result_name, nil, false)
627
673
  end
628
674
  end
629
675
  HALT
@@ -677,9 +723,9 @@ module GraphQL
677
723
  if selection_result
678
724
  if list_type_at_all
679
725
  result_without_errors = value.map { |v| v.is_a?(GraphQL::ExecutionError) ? nil : v }
680
- set_result(selection_result, result_name, result_without_errors)
726
+ set_result(selection_result, result_name, result_without_errors, false)
681
727
  else
682
- set_result(selection_result, result_name, nil)
728
+ set_result(selection_result, result_name, nil, false)
683
729
  end
684
730
  end
685
731
  end
@@ -689,7 +735,7 @@ module GraphQL
689
735
  end
690
736
  when GraphQL::Execution::Interpreter::RawValue
691
737
  # Write raw value directly to the response without resolving nested objects
692
- set_result(selection_result, result_name, value.resolve)
738
+ set_result(selection_result, result_name, value.resolve, false)
693
739
  HALT
694
740
  else
695
741
  value
@@ -717,7 +763,7 @@ module GraphQL
717
763
  rescue StandardError => err
718
764
  schema.handle_or_reraise(context, err)
719
765
  end
720
- set_result(selection_result, result_name, r)
766
+ set_result(selection_result, result_name, r, false)
721
767
  r
722
768
  when "UNION", "INTERFACE"
723
769
  resolved_type_or_lazy = resolve_type(current_type, value)
@@ -735,7 +781,7 @@ module GraphQL
735
781
  err_class = current_type::UnresolvedTypeError
736
782
  type_error = err_class.new(resolved_value, field, parent_type, resolved_type, possible_types)
737
783
  schema.type_error(type_error, context)
738
- set_result(selection_result, result_name, nil)
784
+ set_result(selection_result, result_name, nil, false)
739
785
  nil
740
786
  else
741
787
  continue_field(resolved_value, owner_type, field, resolved_type, ast_node, next_selections, is_non_null, owner_object, arguments, result_name, selection_result)
@@ -751,7 +797,7 @@ module GraphQL
751
797
  continue_value = continue_value(inner_object, owner_type, field, is_non_null, ast_node, result_name, selection_result)
752
798
  if HALT != continue_value
753
799
  response_hash = GraphQLResultHash.new(result_name, selection_result)
754
- set_result(selection_result, result_name, response_hash)
800
+ set_result(selection_result, result_name, response_hash, true)
755
801
  gathered_selections = gather_selections(continue_value, current_type, next_selections)
756
802
  # There are two possibilities for `gathered_selections`:
757
803
  # 1. All selections of this object should be evaluated together (there are no runtime directives modifying execution).
@@ -769,8 +815,13 @@ module GraphQL
769
815
  this_result = response_hash
770
816
  final_result = nil
771
817
  end
772
- # Don't pass `result_name` here because it's already included in the new response hash
773
- set_all_interpreter_context(continue_value, nil, nil, nil, this_result) # reset this mutable state
818
+ # reset this mutable state
819
+ # Unset `result_name` here because it's already included in the new response hash
820
+ st = get_current_runtime_state
821
+ st.current_object = continue_value
822
+ st.current_result_name = nil
823
+ st.current_result = this_result
824
+
774
825
  call_method_on_directives(:resolve, continue_value, selections.graphql_directives) do
775
826
  evaluate_selections(
776
827
  continue_value,
@@ -790,21 +841,21 @@ module GraphQL
790
841
  inner_type = current_type.of_type
791
842
  # This is true for objects, unions, and interfaces
792
843
  use_dataloader_job = !inner_type.unwrap.kind.input?
844
+ inner_type_non_null = inner_type.non_null?
793
845
  response_list = GraphQLResultArray.new(result_name, selection_result)
794
- response_list.graphql_non_null_list_items = inner_type.non_null?
795
- set_result(selection_result, result_name, response_list)
846
+ response_list.graphql_non_null_list_items = inner_type_non_null
847
+ set_result(selection_result, result_name, response_list, true)
796
848
  idx = 0
797
849
  list_value = begin
798
850
  value.each do |inner_value|
799
- break if dead_result?(response_list)
800
851
  this_idx = idx
801
852
  idx += 1
802
853
  if use_dataloader_job
803
854
  @dataloader.append_job do
804
- resolve_list_item(inner_value, inner_type, ast_node, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type)
855
+ resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type)
805
856
  end
806
857
  else
807
- resolve_list_item(inner_value, inner_type, ast_node, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type)
858
+ resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type)
808
859
  end
809
860
  end
810
861
 
@@ -834,12 +885,14 @@ module GraphQL
834
885
  end
835
886
  end
836
887
 
837
- def resolve_list_item(inner_value, inner_type, ast_node, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type) # rubocop:disable Metrics/ParameterLists
838
- set_all_interpreter_context(nil, nil, nil, this_idx, response_list)
888
+ def resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type) # rubocop:disable Metrics/ParameterLists
889
+ st = get_current_runtime_state
890
+ st.current_result_name = this_idx
891
+ st.current_result = response_list
839
892
  call_method_on_directives(:resolve_each, owner_object, ast_node.directives) do
840
893
  # This will update `response_list` with the lazy
841
894
  after_lazy(inner_value, owner: inner_type, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, result_name: this_idx, result: response_list) do |inner_inner_value|
842
- continue_value = continue_value(inner_inner_value, owner_type, field, inner_type.non_null?, ast_node, this_idx, response_list)
895
+ continue_value = continue_value(inner_inner_value, owner_type, field, inner_type_non_null, ast_node, this_idx, response_list)
843
896
  if HALT != continue_value
844
897
  continue_field(continue_value, owner_type, field, inner_type, ast_node, next_selections, false, owner_object, arguments, this_idx, response_list)
845
898
  end
@@ -891,21 +944,8 @@ module GraphQL
891
944
  true
892
945
  end
893
946
 
894
- def set_all_interpreter_context(object, field, arguments, result_name, result)
895
- ti = thread_info
896
- if object
897
- ti[:current_object] = object
898
- end
899
- if field
900
- ti[:current_field] = field
901
- end
902
- if arguments
903
- ti[:current_arguments] = arguments
904
- end
905
- ti[:current_result_name] = result_name
906
- if result
907
- ti[:current_result] = result
908
- end
947
+ def get_current_runtime_state
948
+ Thread.current[:__graphql_runtime_info] ||= CurrentState.new
909
949
  end
910
950
 
911
951
  # @param obj [Object] Some user-returned value that may want to be batched
@@ -917,7 +957,12 @@ module GraphQL
917
957
  if lazy?(lazy_obj)
918
958
  orig_result = result
919
959
  lazy = GraphQL::Execution::Lazy.new(field: field) do
920
- set_all_interpreter_context(owner_object, field, arguments, result_name, orig_result)
960
+ st = get_current_runtime_state
961
+ st.current_object = owner_object
962
+ st.current_field = field
963
+ st.current_arguments = arguments
964
+ st.current_result_name = result_name
965
+ st.current_result = orig_result
921
966
  # Wrap the execution of _this_ method with tracing,
922
967
  # but don't wrap the continuation below
923
968
  inner_obj = begin
@@ -943,7 +988,7 @@ module GraphQL
943
988
  if eager
944
989
  lazy.value
945
990
  else
946
- set_result(result, result_name, lazy)
991
+ set_result(result, result_name, lazy, false)
947
992
  current_depth = 0
948
993
  while result
949
994
  current_depth += 1
@@ -953,7 +998,7 @@ module GraphQL
953
998
  lazy
954
999
  end
955
1000
  else
956
- set_all_interpreter_context(owner_object, field, arguments, result_name, result)
1001
+ # Don't need to reset state here because it _wasn't_ lazy.
957
1002
  yield(lazy_obj)
958
1003
  end
959
1004
  end
@@ -968,13 +1013,7 @@ module GraphQL
968
1013
  end
969
1014
 
970
1015
  def delete_all_interpreter_context
971
- if (ti = thread_info)
972
- ti.delete(:current_result)
973
- ti.delete(:current_result_name)
974
- ti.delete(:current_field)
975
- ti.delete(:current_object)
976
- ti.delete(:current_arguments)
977
- end
1016
+ Thread.current[:__graphql_runtime_info] = nil
978
1017
  end
979
1018
 
980
1019
  def resolve_type(type, value)
@@ -14,7 +14,7 @@ module GraphQL
14
14
  class << self
15
15
  # Used internally to signal that the query shouldn't be executed
16
16
  # @api private
17
- NO_OPERATION = {}.freeze
17
+ NO_OPERATION = GraphQL::EmptyObjects::EMPTY_HASH
18
18
 
19
19
  # @param schema [GraphQL::Schema]
20
20
  # @param queries [Array<GraphQL::Query, Hash>]
@@ -1,8 +1,13 @@
1
1
  # frozen_string_literal: true
2
+ require "graphql/deprecation"
3
+
2
4
  module GraphQL
3
5
  # @api private
4
6
  class Filter
5
- def initialize(only: nil, except: nil)
7
+ def initialize(only: nil, except: nil, silence_deprecation_warning: false)
8
+ if !silence_deprecation_warning
9
+ GraphQL::Deprecation.warn("GraphQL::Filter is deprecated and will be removed in v2.1.0. Implement `visible?` on your schema members instead (https://graphql-ruby.org/authorization/visibility.html).")
10
+ end
6
11
  @only = only
7
12
  @except = except
8
13
  end
@@ -17,7 +22,7 @@ module GraphQL
17
22
  onlies = [self].concat(Array(only))
18
23
  merged_only = MergedOnly.build(onlies)
19
24
  merged_except = MergedExcept.build(Array(except))
20
- self.class.new(only: merged_only, except: merged_except)
25
+ self.class.new(only: merged_only, except: merged_except, silence_deprecation_warning: true)
21
26
  end
22
27
 
23
28
  private