graphql 2.0.20 → 2.0.21

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