graphql 2.0.21 → 2.0.22

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.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 43f1cc3ef36a54a31f8afa03ee3109b34f046e26c427acfb80599a2dd245cf14
4
- data.tar.gz: 4e4a15c766650e36004175de15cfcb9f67c6b4e7d24e2f91712a6ad3f8681453
3
+ metadata.gz: 2564f5f0788db163950385832a4255c88b47b72ddb45175ec30f0f42c6dc0b04
4
+ data.tar.gz: c1760c1a43453ece3baed09bde3e30f6012bd4357ec597781e4616a7c67a2bc4
5
5
  SHA512:
6
- metadata.gz: 908f3790bcc773bb6c7fe3db1f5e2ccabf361ece7b462b80fc4e59911167eabb8baf544739f31ac425579bca61b56b46937ac60e7a54a03827228016457388de
7
- data.tar.gz: 62fb9dc90ab55aff66d3d1621fc28b18c0c9599e2f8a7f8521d372a6f549c85ac0a2e50a0d43cb77bde728cb44f85b178bb37388c6bd3d2aed5f8e91723a79ef
6
+ metadata.gz: 4e1d1747ceb4450228b9b7cb9b123f61585fe8eea3d6442627b56818f4a146e2b507e5a0b8665e25334a64dca79eb8a089c2aa0ff3e8d710fb6adec080623da0
7
+ data.tar.gz: f1ec3b93d97f4546184496aed5bc03d5ba1a5af1655aaeeb40ac20461e7c722431a116ca8e1a019d8b7e7eda59e7748f862985ea1dd131b3005ceb9ca3c9fa7c
@@ -7,49 +7,50 @@ module GraphQL
7
7
  def initialize(query)
8
8
  @query = query
9
9
  @dataloader = query.context.dataloader
10
- @storage = Hash.new do |h, ast_node|
11
- h[ast_node] = Hash.new do |h2, arg_owner|
12
- h2[arg_owner] = Hash.new do |h3, parent_object|
13
- dataload_for(ast_node, arg_owner, parent_object) do |kwarg_arguments|
14
- h3[parent_object] = @query.schema.after_lazy(kwarg_arguments) do |resolved_args|
15
- h3[parent_object] = resolved_args
16
- end
17
- end
18
-
19
- if !h3.key?(parent_object)
20
- # TODO should i bother putting anything here?
21
- h3[parent_object] = NO_ARGUMENTS
22
- else
23
- h3[parent_object]
24
- end
10
+ @storage = Hash.new do |h, argument_owner|
11
+ args_by_parent = if argument_owner.arguments_statically_coercible?
12
+ shared_values_cache = {}
13
+ Hash.new do |h2, ignored_parent_object|
14
+ h2[ignored_parent_object] = shared_values_cache
15
+ end
16
+ else
17
+ Hash.new do |h2, parent_object|
18
+ args_by_node = {}
19
+ args_by_node.compare_by_identity
20
+ h2[parent_object] = args_by_node
25
21
  end
26
22
  end
23
+ args_by_parent.compare_by_identity
24
+ h[argument_owner] = args_by_parent
27
25
  end
26
+ @storage.compare_by_identity
28
27
  end
29
28
 
30
29
  def fetch(ast_node, argument_owner, parent_object)
31
- # If any jobs were enqueued, run them now,
32
- # since this might have been called outside of execution.
33
- # (The jobs are responsible for updating `result` in-place.)
34
- if !@storage.key?(ast_node) || !@storage[ast_node].key?(argument_owner)
35
- @dataloader.run_isolated do
36
- @storage[ast_node][argument_owner][parent_object]
30
+ # This runs eagerly if no block is given
31
+ @storage[argument_owner][parent_object][ast_node] ||= begin
32
+ args_hash = self.class.prepare_args_hash(@query, ast_node)
33
+ kwarg_arguments = argument_owner.coerce_arguments(parent_object, args_hash, @query.context)
34
+ @query.after_lazy(kwarg_arguments) do |resolved_args|
35
+ @storage[argument_owner][parent_object][ast_node] = resolved_args
37
36
  end
38
37
  end
39
- # Ack, the _hash_ is updated, but the key is eventually
40
- # overridden with an immutable arguments instance.
41
- # The first call queues up the job,
42
- # then this call fetches the result.
43
- # TODO this should be better, find a solution
44
- # that works with merging the runtime.rb code
45
- @storage[ast_node][argument_owner][parent_object]
38
+
46
39
  end
47
40
 
48
41
  # @yield [Interpreter::Arguments, Lazy<Interpreter::Arguments>] The finally-loaded arguments
49
42
  def dataload_for(ast_node, argument_owner, parent_object, &block)
50
43
  # First, normalize all AST or Ruby values to a plain Ruby hash
51
- args_hash = self.class.prepare_args_hash(@query, ast_node)
52
- argument_owner.coerce_arguments(parent_object, args_hash, @query.context, &block)
44
+ arg_storage = @storage[argument_owner][parent_object]
45
+ if (args = arg_storage[ast_node])
46
+ yield(args)
47
+ else
48
+ args_hash = self.class.prepare_args_hash(@query, ast_node)
49
+ argument_owner.coerce_arguments(parent_object, args_hash, @query.context) do |resolved_args|
50
+ arg_storage[ast_node] = resolved_args
51
+ yield(resolved_args)
52
+ end
53
+ end
53
54
  nil
54
55
  end
55
56
 
@@ -22,12 +22,13 @@ module GraphQL
22
22
  end
23
23
 
24
24
  module GraphQLResult
25
- def initialize(result_name, parent_result)
25
+ def initialize(result_name, parent_result, is_non_null_in_parent)
26
26
  @graphql_parent = parent_result
27
27
  if parent_result && parent_result.graphql_dead
28
28
  @graphql_dead = true
29
29
  end
30
30
  @graphql_result_name = result_name
31
+ @graphql_is_non_null_in_parent = is_non_null_in_parent
31
32
  # Jump through some hoops to avoid creating this duplicate storage if at all possible.
32
33
  @graphql_metadata = nil
33
34
  end
@@ -42,22 +43,14 @@ module GraphQL
42
43
  end
43
44
 
44
45
  attr_accessor :graphql_dead
45
- attr_reader :graphql_parent, :graphql_result_name
46
-
47
- # Although these are used by only one of the Result classes,
48
- # it's handy to have the methods implemented on both (even though they just return `nil`)
49
- # because it makes it easy to check if anything is assigned.
50
- # @return [nil, Array<String>]
51
- attr_accessor :graphql_non_null_field_names
52
- # @return [nil, true]
53
- attr_accessor :graphql_non_null_list_items
46
+ attr_reader :graphql_parent, :graphql_result_name, :graphql_is_non_null_in_parent
54
47
 
55
48
  # @return [Hash] Plain-Ruby result data (`@graphql_metadata` contains Result wrapper objects)
56
49
  attr_accessor :graphql_result_data
57
50
  end
58
51
 
59
52
  class GraphQLResultHash
60
- def initialize(_result_name, _parent_result)
53
+ def initialize(_result_name, _parent_result, _is_non_null_in_parent)
61
54
  super
62
55
  @graphql_result_data = {}
63
56
  end
@@ -145,7 +138,7 @@ module GraphQL
145
138
  class GraphQLResultArray
146
139
  include GraphQLResult
147
140
 
148
- def initialize(_result_name, _parent_result)
141
+ def initialize(_result_name, _parent_result, _is_non_null_in_parent)
149
142
  super
150
143
  @graphql_result_data = []
151
144
  end
@@ -189,10 +182,6 @@ module GraphQL
189
182
  end
190
183
  end
191
184
 
192
- class GraphQLSelectionSet < Hash
193
- attr_accessor :graphql_directives
194
- end
195
-
196
185
  # @return [GraphQL::Query]
197
186
  attr_reader :query
198
187
 
@@ -204,14 +193,12 @@ module GraphQL
204
193
 
205
194
  def initialize(query:, lazies_at_depth:)
206
195
  @query = query
196
+ @current_trace = query.current_trace
207
197
  @dataloader = query.multiplex.dataloader
208
198
  @lazies_at_depth = lazies_at_depth
209
199
  @schema = query.schema
210
200
  @context = query.context
211
- @multiplex_context = query.multiplex.context
212
- # Start this off empty:
213
- Thread.current[:__graphql_runtime_info] = nil
214
- @response = GraphQLResultHash.new(nil, nil)
201
+ @response = GraphQLResultHash.new(nil, nil, false)
215
202
  # Identify runtime directives by checking which of this schema's directives have overridden `def self.resolve`
216
203
  @runtime_directive_names = []
217
204
  noop_resolve_owner = GraphQL::Schema::Directive.singleton_class
@@ -225,8 +212,11 @@ module GraphQL
225
212
  # Which assumes that MyObject.get_field("myField") will return the same field
226
213
  # during the lifetime of a query
227
214
  @fields_cache = Hash.new { |h, k| h[k] = {} }
215
+ # this can by by-identity since owners are the same object, but not the sub-hash, which uses strings.
216
+ @fields_cache.compare_by_identity
228
217
  # { Class => Boolean }
229
218
  @lazy_cache = {}
219
+ @lazy_cache.compare_by_identity
230
220
  end
231
221
 
232
222
  def final_result
@@ -275,7 +265,7 @@ module GraphQL
275
265
  # directly evaluated and the results can be written right into the main response hash.
276
266
  tap_or_each(gathered_selections) do |selections, is_selection_array|
277
267
  if is_selection_array
278
- selection_response = GraphQLResultHash.new(nil, nil)
268
+ selection_response = GraphQLResultHash.new(nil, nil, false)
279
269
  final_response = @response
280
270
  else
281
271
  selection_response = @response
@@ -286,8 +276,11 @@ module GraphQL
286
276
  st = get_current_runtime_state
287
277
  st.current_object = query.root_value
288
278
  st.current_result = selection_response
289
-
290
- call_method_on_directives(:resolve, object_proxy, selections.graphql_directives) do
279
+ # This is a less-frequent case; use a fast check since it's often not there.
280
+ if (directives = selections[:graphql_directives])
281
+ selections.delete(:graphql_directives)
282
+ end
283
+ call_method_on_directives(:resolve, object_proxy, directives) do
291
284
  evaluate_selections(
292
285
  object_proxy,
293
286
  root_type,
@@ -306,7 +299,7 @@ module GraphQL
306
299
  nil
307
300
  end
308
301
 
309
- def gather_selections(owner_object, owner_type, selections, selections_to_run = nil, selections_by_name = GraphQLSelectionSet.new)
302
+ def gather_selections(owner_object, owner_type, selections, selections_to_run = nil, selections_by_name = {})
310
303
  selections.each do |node|
311
304
  # Skip gathering this if the directive says so
312
305
  if !directives_include?(node, owner_object, owner_type)
@@ -332,8 +325,8 @@ module GraphQL
332
325
  else
333
326
  # This is an InlineFragment or a FragmentSpread
334
327
  if @runtime_directive_names.any? && node.directives.any? { |d| @runtime_directive_names.include?(d.name) }
335
- next_selections = GraphQLSelectionSet.new
336
- next_selections.graphql_directives = node.directives
328
+ next_selections = {}
329
+ next_selections[:graphql_directives] = node.directives
337
330
  if selections_to_run
338
331
  selections_to_run << next_selections
339
332
  else
@@ -400,6 +393,12 @@ module GraphQL
400
393
  selections_result.merge_into(target_result)
401
394
  end
402
395
  }
396
+ # Field resolution may pause the fiber,
397
+ # so it wouldn't get to the `Resolve` call that happens below.
398
+ # So instead trigger a run from this outer context.
399
+ if is_eager_selection
400
+ @dataloader.run
401
+ end
403
402
  end
404
403
 
405
404
  selections_result
@@ -441,9 +440,6 @@ module GraphQL
441
440
  # the field's return type at this path in order
442
441
  # to propagate `null`
443
442
  return_type_non_null = return_type.non_null?
444
- if return_type_non_null
445
- (selections_result.graphql_non_null_field_names ||= []).push(result_name)
446
- end
447
443
  # Set this before calling `run_with_directives`, so that the directive can have the latest path
448
444
  st = get_current_runtime_state
449
445
  st.current_field = field_defn
@@ -474,7 +470,7 @@ module GraphQL
474
470
  end
475
471
 
476
472
  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
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|
473
+ after_lazy(arguments, field: field_defn, ast_node: ast_node, owner_object: object, arguments: arguments, result_name: result_name, result: selection_result) do |resolved_arguments|
478
474
  if resolved_arguments.is_a?(GraphQL::ExecutionError) || resolved_arguments.is_a?(GraphQL::UnauthorizedError)
479
475
  continue_value(resolved_arguments, owner_type, field_defn, return_type_non_null, ast_node, result_name, selection_result)
480
476
  next
@@ -552,7 +548,7 @@ module GraphQL
552
548
  field_result = call_method_on_directives(:resolve, object, directives) do
553
549
  # Actually call the field resolver and capture the result
554
550
  app_result = begin
555
- query.current_trace.execute_field(field: field_defn, ast_node: ast_node, query: query, object: object, arguments: kwarg_arguments) do
551
+ @current_trace.execute_field(field: field_defn, ast_node: ast_node, query: query, object: object, arguments: kwarg_arguments) do
556
552
  field_defn.resolve(object, kwarg_arguments, context)
557
553
  end
558
554
  rescue GraphQL::ExecutionError => err
@@ -564,7 +560,7 @@ module GraphQL
564
560
  ex_err
565
561
  end
566
562
  end
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|
563
+ after_lazy(app_result, field: field_defn, ast_node: ast_node, owner_object: object, arguments: resolved_arguments, result_name: result_name, result: selection_result) do |inner_result|
568
564
  continue_value = continue_value(inner_result, owner_type, field_defn, return_type_non_null, ast_node, result_name, selection_result)
569
565
  if HALT != continue_value
570
566
  continue_field(continue_value, owner_type, field_defn, return_type, ast_node, next_selections, false, object, resolved_arguments, result_name, selection_result)
@@ -588,13 +584,9 @@ module GraphQL
588
584
  selection_result.graphql_dead # || ((parent = selection_result.graphql_parent) && parent.graphql_dead)
589
585
  end
590
586
 
591
- def set_result(selection_result, result_name, value, is_child_result)
587
+ def set_result(selection_result, result_name, value, is_child_result, is_non_null)
592
588
  if !dead_result?(selection_result)
593
- if value.nil? &&
594
- ( # there are two conditions under which `nil` is not allowed in the response:
595
- (selection_result.graphql_non_null_list_items) || # this value would be written into a list that doesn't allow nils
596
- ((nn = selection_result.graphql_non_null_field_names) && nn.include?(result_name)) # this value would be written into a field that doesn't allow nils
597
- )
589
+ if value.nil? && is_non_null
598
590
  # This is an invalid nil that should be propagated
599
591
  # One caller of this method passes a block,
600
592
  # namely when application code returns a `nil` to GraphQL and it doesn't belong there.
@@ -604,11 +596,12 @@ module GraphQL
604
596
  # TODO the code is trying to tell me something.
605
597
  yield if block_given?
606
598
  parent = selection_result.graphql_parent
607
- name_in_parent = selection_result.graphql_result_name
608
599
  if parent.nil? # This is a top-level result hash
609
600
  @response = nil
610
601
  else
611
- set_result(parent, name_in_parent, nil, false)
602
+ name_in_parent = selection_result.graphql_result_name
603
+ is_non_null_in_parent = selection_result.graphql_is_non_null_in_parent
604
+ set_result(parent, name_in_parent, nil, false, is_non_null_in_parent)
612
605
  set_graphql_dead(selection_result)
613
606
  end
614
607
  elsif is_child_result
@@ -650,13 +643,13 @@ module GraphQL
650
643
  case value
651
644
  when nil
652
645
  if is_non_null
653
- set_result(selection_result, result_name, nil, false) do
646
+ set_result(selection_result, result_name, nil, false, is_non_null) do
654
647
  # This block is called if `result_name` is not dead. (Maybe a previous invalid nil caused it be marked dead.)
655
648
  err = parent_type::InvalidNullError.new(parent_type, field, value)
656
649
  schema.type_error(err, context)
657
650
  end
658
651
  else
659
- set_result(selection_result, result_name, nil, false)
652
+ set_result(selection_result, result_name, nil, false, is_non_null)
660
653
  end
661
654
  HALT
662
655
  when GraphQL::Error
@@ -669,7 +662,7 @@ module GraphQL
669
662
  value.ast_node ||= ast_node
670
663
  context.errors << value
671
664
  if selection_result
672
- set_result(selection_result, result_name, nil, false)
665
+ set_result(selection_result, result_name, nil, false, is_non_null)
673
666
  end
674
667
  end
675
668
  HALT
@@ -723,9 +716,9 @@ module GraphQL
723
716
  if selection_result
724
717
  if list_type_at_all
725
718
  result_without_errors = value.map { |v| v.is_a?(GraphQL::ExecutionError) ? nil : v }
726
- set_result(selection_result, result_name, result_without_errors, false)
719
+ set_result(selection_result, result_name, result_without_errors, false, is_non_null)
727
720
  else
728
- set_result(selection_result, result_name, nil, false)
721
+ set_result(selection_result, result_name, nil, false, is_non_null)
729
722
  end
730
723
  end
731
724
  end
@@ -735,7 +728,7 @@ module GraphQL
735
728
  end
736
729
  when GraphQL::Execution::Interpreter::RawValue
737
730
  # Write raw value directly to the response without resolving nested objects
738
- set_result(selection_result, result_name, value.resolve, false)
731
+ set_result(selection_result, result_name, value.resolve, false, is_non_null)
739
732
  HALT
740
733
  else
741
734
  value
@@ -763,11 +756,11 @@ module GraphQL
763
756
  rescue StandardError => err
764
757
  schema.handle_or_reraise(context, err)
765
758
  end
766
- set_result(selection_result, result_name, r, false)
759
+ set_result(selection_result, result_name, r, false, is_non_null)
767
760
  r
768
761
  when "UNION", "INTERFACE"
769
762
  resolved_type_or_lazy = resolve_type(current_type, value)
770
- after_lazy(resolved_type_or_lazy, owner: current_type, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, trace: false, result_name: result_name, result: selection_result) do |resolved_type_result|
763
+ after_lazy(resolved_type_or_lazy, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, trace: false, result_name: result_name, result: selection_result) do |resolved_type_result|
771
764
  if resolved_type_result.is_a?(Array) && resolved_type_result.length == 2
772
765
  resolved_type, resolved_value = resolved_type_result
773
766
  else
@@ -781,7 +774,7 @@ module GraphQL
781
774
  err_class = current_type::UnresolvedTypeError
782
775
  type_error = err_class.new(resolved_value, field, parent_type, resolved_type, possible_types)
783
776
  schema.type_error(type_error, context)
784
- set_result(selection_result, result_name, nil, false)
777
+ set_result(selection_result, result_name, nil, false, is_non_null)
785
778
  nil
786
779
  else
787
780
  continue_field(resolved_value, owner_type, field, resolved_type, ast_node, next_selections, is_non_null, owner_object, arguments, result_name, selection_result)
@@ -793,11 +786,11 @@ module GraphQL
793
786
  rescue GraphQL::ExecutionError => err
794
787
  err
795
788
  end
796
- after_lazy(object_proxy, owner: current_type, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, trace: false, result_name: result_name, result: selection_result) do |inner_object|
789
+ after_lazy(object_proxy, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, trace: false, result_name: result_name, result: selection_result) do |inner_object|
797
790
  continue_value = continue_value(inner_object, owner_type, field, is_non_null, ast_node, result_name, selection_result)
798
791
  if HALT != continue_value
799
- response_hash = GraphQLResultHash.new(result_name, selection_result)
800
- set_result(selection_result, result_name, response_hash, true)
792
+ response_hash = GraphQLResultHash.new(result_name, selection_result, is_non_null)
793
+ set_result(selection_result, result_name, response_hash, true, is_non_null)
801
794
  gathered_selections = gather_selections(continue_value, current_type, next_selections)
802
795
  # There are two possibilities for `gathered_selections`:
803
796
  # 1. All selections of this object should be evaluated together (there are no runtime directives modifying execution).
@@ -809,7 +802,7 @@ module GraphQL
809
802
  # (Technically, it's possible that one of those entries _doesn't_ require isolation.)
810
803
  tap_or_each(gathered_selections) do |selections, is_selection_array|
811
804
  if is_selection_array
812
- this_result = GraphQLResultHash.new(result_name, selection_result)
805
+ this_result = GraphQLResultHash.new(result_name, selection_result, is_non_null)
813
806
  final_result = response_hash
814
807
  else
815
808
  this_result = response_hash
@@ -822,7 +815,11 @@ module GraphQL
822
815
  st.current_result_name = nil
823
816
  st.current_result = this_result
824
817
 
825
- call_method_on_directives(:resolve, continue_value, selections.graphql_directives) do
818
+ # This is a less-frequent case; use a fast check since it's often not there.
819
+ if (directives = selections[:graphql_directives])
820
+ selections.delete(:graphql_directives)
821
+ end
822
+ call_method_on_directives(:resolve, continue_value, directives) do
826
823
  evaluate_selections(
827
824
  continue_value,
828
825
  current_type,
@@ -842,12 +839,12 @@ module GraphQL
842
839
  # This is true for objects, unions, and interfaces
843
840
  use_dataloader_job = !inner_type.unwrap.kind.input?
844
841
  inner_type_non_null = inner_type.non_null?
845
- response_list = GraphQLResultArray.new(result_name, selection_result)
846
- response_list.graphql_non_null_list_items = inner_type_non_null
847
- set_result(selection_result, result_name, response_list, true)
848
- idx = 0
842
+ response_list = GraphQLResultArray.new(result_name, selection_result, is_non_null)
843
+ set_result(selection_result, result_name, response_list, true, is_non_null)
844
+ idx = nil
849
845
  list_value = begin
850
846
  value.each do |inner_value|
847
+ idx ||= 0
851
848
  this_idx = idx
852
849
  idx += 1
853
850
  if use_dataloader_job
@@ -878,8 +875,9 @@ module GraphQL
878
875
  ex_err
879
876
  end
880
877
  end
881
-
882
- continue_value(list_value, owner_type, field, inner_type.non_null?, ast_node, result_name, selection_result)
878
+ # Detect whether this error came while calling `.each` (before `idx` is set) or while running list *items* (after `idx` is set)
879
+ error_is_non_null = idx.nil? ? is_non_null : inner_type.non_null?
880
+ continue_value(list_value, owner_type, field, error_is_non_null, ast_node, result_name, selection_result)
883
881
  else
884
882
  raise "Invariant: Unhandled type kind #{current_type.kind} (#{current_type})"
885
883
  end
@@ -891,7 +889,7 @@ module GraphQL
891
889
  st.current_result = response_list
892
890
  call_method_on_directives(:resolve_each, owner_object, ast_node.directives) do
893
891
  # This will update `response_list` with the lazy
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|
892
+ after_lazy(inner_value, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, result_name: this_idx, result: response_list) do |inner_inner_value|
895
893
  continue_value = continue_value(inner_inner_value, owner_type, field, inner_type_non_null, ast_node, this_idx, response_list)
896
894
  if HALT != continue_value
897
895
  continue_field(continue_value, owner_type, field, inner_type, ast_node, next_selections, false, owner_object, arguments, this_idx, response_list)
@@ -945,7 +943,25 @@ module GraphQL
945
943
  end
946
944
 
947
945
  def get_current_runtime_state
948
- Thread.current[:__graphql_runtime_info] ||= CurrentState.new
946
+ current_state = Thread.current[:__graphql_runtime_info] ||= begin
947
+ per_query_state = {}
948
+ per_query_state.compare_by_identity
949
+ per_query_state
950
+ end
951
+
952
+ current_state[@query] ||= CurrentState.new
953
+ end
954
+
955
+ def minimal_after_lazy(value, &block)
956
+ if lazy?(value)
957
+ GraphQL::Execution::Lazy.new do
958
+ result = @schema.sync_lazy(value)
959
+ # The returned result might also be lazy, so check it, too
960
+ minimal_after_lazy(result, &block)
961
+ end
962
+ else
963
+ yield(value)
964
+ end
949
965
  end
950
966
 
951
967
  # @param obj [Object] Some user-returned value that may want to be batched
@@ -953,7 +969,7 @@ module GraphQL
953
969
  # @param eager [Boolean] Set to `true` for mutation root fields only
954
970
  # @param trace [Boolean] If `false`, don't wrap this with field tracing
955
971
  # @return [GraphQL::Execution::Lazy, Object] If loading `object` will be deferred, it's a wrapper over it.
956
- def after_lazy(lazy_obj, owner:, field:, owner_object:, arguments:, ast_node:, result:, result_name:, eager: false, trace: true, &block)
972
+ def after_lazy(lazy_obj, field:, owner_object:, arguments:, ast_node:, result:, result_name:, eager: false, trace: true, &block)
957
973
  if lazy?(lazy_obj)
958
974
  orig_result = result
959
975
  lazy = GraphQL::Execution::Lazy.new(field: field) do
@@ -967,7 +983,7 @@ module GraphQL
967
983
  # but don't wrap the continuation below
968
984
  inner_obj = begin
969
985
  if trace
970
- query.current_trace.execute_field_lazy(field: field, query: query, object: owner_object, arguments: arguments, ast_node: ast_node) do
986
+ @current_trace.execute_field_lazy(field: field, query: query, object: owner_object, arguments: arguments, ast_node: ast_node) do
971
987
  schema.sync_lazy(lazy_obj)
972
988
  end
973
989
  else
@@ -988,7 +1004,7 @@ module GraphQL
988
1004
  if eager
989
1005
  lazy.value
990
1006
  else
991
- set_result(result, result_name, lazy, false)
1007
+ set_result(result, result_name, lazy, false, false) # is_non_null is irrelevant here
992
1008
  current_depth = 0
993
1009
  while result
994
1010
  current_depth += 1
@@ -1013,17 +1029,24 @@ module GraphQL
1013
1029
  end
1014
1030
 
1015
1031
  def delete_all_interpreter_context
1016
- Thread.current[:__graphql_runtime_info] = nil
1032
+ per_query_state = Thread.current[:__graphql_runtime_info]
1033
+ if per_query_state
1034
+ per_query_state.delete(@query)
1035
+ if per_query_state.size == 0
1036
+ Thread.current[:__graphql_runtime_info] = nil
1037
+ end
1038
+ end
1039
+ nil
1017
1040
  end
1018
1041
 
1019
1042
  def resolve_type(type, value)
1020
- resolved_type, resolved_value = query.current_trace.resolve_type(query: query, type: type, object: value) do
1043
+ resolved_type, resolved_value = @current_trace.resolve_type(query: query, type: type, object: value) do
1021
1044
  query.resolve_type(type, value)
1022
1045
  end
1023
1046
 
1024
1047
  if lazy?(resolved_type)
1025
1048
  GraphQL::Execution::Lazy.new do
1026
- query.current_trace.resolve_type_lazy(query: query, type: type, object: value) do
1049
+ @current_trace.resolve_type_lazy(query: query, type: type, object: value) do
1027
1050
  schema.sync_lazy(resolved_type)
1028
1051
  end
1029
1052
  end
@@ -1037,9 +1060,12 @@ module GraphQL
1037
1060
  end
1038
1061
 
1039
1062
  def lazy?(object)
1040
- @lazy_cache.fetch(object.class) {
1041
- @lazy_cache[object.class] = @schema.lazy?(object)
1042
- }
1063
+ obj_class = object.class
1064
+ is_lazy = @lazy_cache[obj_class]
1065
+ if is_lazy.nil?
1066
+ is_lazy = @lazy_cache[obj_class] = @schema.lazy?(object)
1067
+ end
1068
+ is_lazy
1043
1069
  end
1044
1070
  end
1045
1071
  end
@@ -87,7 +87,6 @@ module GraphQL
87
87
 
88
88
  # Then, work through lazy results in a breadth-first way
89
89
  multiplex.dataloader.append_job {
90
- tracer = multiplex
91
90
  query = multiplex.queries.length == 1 ? multiplex.queries[0] : nil
92
91
  queries = multiplex ? multiplex.queries : [query]
93
92
  final_values = queries.map do |query|
@@ -96,7 +95,7 @@ module GraphQL
96
95
  runtime ? runtime.final_result : nil
97
96
  end
98
97
  final_values.compact!
99
- tracer.current_trace.execute_query_lazy(multiplex: multiplex, query: query) do
98
+ multiplex.current_trace.execute_query_lazy(multiplex: multiplex, query: query) do
100
99
  Interpreter::Resolve.resolve_each_depth(lazies_at_depth, multiplex.dataloader)
101
100
  end
102
101
  queries.each do |query|
@@ -55,7 +55,7 @@ module GraphQL
55
55
  @arguments
56
56
  else
57
57
  @arguments = if @field
58
- @query.schema.after_lazy(@query.arguments_for(@ast_nodes.first, @field)) do |args|
58
+ @query.after_lazy(@query.arguments_for(@ast_nodes.first, @field)) do |args|
59
59
  args.is_a?(Execution::Interpreter::Arguments) ? args.keyword_arguments : args
60
60
  end
61
61
  else
@@ -6,7 +6,8 @@ module GraphQL
6
6
  class Filter
7
7
  def initialize(only: nil, except: nil, silence_deprecation_warning: false)
8
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).")
9
+ line = caller(2, 10).find { |l| !l.include?("lib/graphql") }
10
+ GraphQL::Deprecation.warn("GraphQL::Filter, `only:`, `except:`, and `.merge_filters` are deprecated and will be removed in v2.1.0. Implement `visible?` on your schema members instead (https://graphql-ruby.org/authorization/visibility.html).\n #{line}")
10
11
  end
11
12
  @only = only
12
13
  @except = except
@@ -24,17 +24,21 @@ module GraphQL
24
24
  @include_built_in_directives = include_built_in_directives
25
25
  @include_one_of = false
26
26
 
27
- filter = GraphQL::Filter.new(only: only, except: except, silence_deprecation_warning: true)
28
- if @schema.respond_to?(:visible?)
29
- filter = filter.merge(only: @schema.method(:visible?))
27
+ schema_context = schema.context_class.new(query: nil, object: nil, schema: schema, values: context)
28
+
29
+ @warden = if only || except
30
+ filter = GraphQL::Filter
31
+ .new(only: only, except: except)
32
+ .merge(only: @schema.method(:visible?))
33
+ GraphQL::Schema::Warden.new(
34
+ filter,
35
+ schema: @schema,
36
+ context: schema_context,
37
+ )
38
+ else
39
+ GraphQL::Schema::Warden.new(schema: @schema, context: schema_context)
30
40
  end
31
41
 
32
- schema_context = schema.context_class.new(query: nil, object: nil, schema: schema, values: context)
33
- @warden = GraphQL::Schema::Warden.new(
34
- filter,
35
- schema: @schema,
36
- context: schema_context,
37
- )
38
42
  schema_context.warden = @warden
39
43
  end
40
44
 
@@ -95,12 +95,14 @@ module GraphQL
95
95
  previous_token: nil,
96
96
  }
97
97
 
98
- unless string.valid_encoding?
99
- emit(:BAD_UNICODE_ESCAPE, 0, 0, meta, string)
98
+ value = string.dup.force_encoding(Encoding::UTF_8)
99
+
100
+ unless value.valid_encoding?
101
+ emit(:BAD_UNICODE_ESCAPE, 0, 0, meta, value)
100
102
  return meta[:tokens]
101
103
  end
102
104
 
103
- scan = StringScanner.new string
105
+ scan = StringScanner.new value
104
106
 
105
107
  while !scan.eos?
106
108
  pos = scan.pos
@@ -227,7 +227,8 @@ module GraphQL
227
227
  current_path
228
228
  else
229
229
  (current_runtime_state = Thread.current[:__graphql_runtime_info]) &&
230
- (current_runtime_state.public_send(key))
230
+ (query_runtime_state = current_runtime_state[@query]) &&
231
+ (query_runtime_state.public_send(key))
231
232
  end
232
233
  else
233
234
  # not found
@@ -237,10 +238,12 @@ module GraphQL
237
238
 
238
239
  def current_path
239
240
  current_runtime_state = Thread.current[:__graphql_runtime_info]
240
- path = current_runtime_state &&
241
- (result = current_runtime_state.current_result) &&
241
+ query_runtime_state = current_runtime_state && current_runtime_state[@query]
242
+
243
+ path = query_runtime_state &&
244
+ (result = query_runtime_state.current_result) &&
242
245
  (result.path)
243
- if path && (rn = current_runtime_state.current_result_name)
246
+ if path && (rn = query_runtime_state.current_result_name)
244
247
  path = path.dup
245
248
  path.push(rn)
246
249
  end
@@ -260,7 +263,8 @@ module GraphQL
260
263
  def fetch(key, default = UNSPECIFIED_FETCH_DEFAULT)
261
264
  if RUNTIME_METADATA_KEYS.include?(key)
262
265
  (runtime = Thread.current[:__graphql_runtime_info]) &&
263
- (runtime.public_send(key))
266
+ (query_runtime_state = runtime[@query]) &&
267
+ (query_runtime_state.public_send(key))
264
268
  elsif @scoped_context.key?(key)
265
269
  scoped_context[key]
266
270
  elsif @provided_values.key?(key)
@@ -277,8 +281,13 @@ module GraphQL
277
281
  def dig(key, *other_keys)
278
282
  if RUNTIME_METADATA_KEYS.include?(key)
279
283
  (current_runtime_state = Thread.current[:__graphql_runtime_info]) &&
280
- (obj = current_runtime_state.public_send(key)) &&
281
- obj.dig(*other_keys)
284
+ (query_runtime_state = current_runtime_state[@query]) &&
285
+ (obj = query_runtime_state.public_send(key)) &&
286
+ if other_keys.empty?
287
+ obj
288
+ else
289
+ obj.dig(*other_keys)
290
+ end
282
291
  elsif @scoped_context.key?(key)
283
292
  @scoped_context.dig(key, *other_keys)
284
293
  else
@@ -12,6 +12,9 @@ module GraphQL
12
12
  end
13
13
 
14
14
  class NullQuery
15
+ def after_lazy(value)
16
+ yield(value)
17
+ end
15
18
  end
16
19
 
17
20
  class NullSchema < GraphQL::Schema
data/lib/graphql/query.rb CHANGED
@@ -87,7 +87,9 @@ module GraphQL
87
87
  # Even if `variables: nil` is passed, use an empty hash for simpler logic
88
88
  variables ||= {}
89
89
  @schema = schema
90
- @filter = schema.default_filter.merge(except: except, only: only)
90
+ if only || except
91
+ merge_filters(except: except, only: only)
92
+ end
91
93
  @context = schema.context_class.new(query: self, object: root_value, values: context)
92
94
  @warden = warden
93
95
  @subscription_topic = subscription_topic
@@ -151,11 +153,6 @@ module GraphQL
151
153
 
152
154
  @result_values = nil
153
155
  @executed = false
154
-
155
- # TODO add a general way to define schema-level filters
156
- if @schema.respond_to?(:visible?)
157
- merge_filters(only: @schema.method(:visible?))
158
- end
159
156
  end
160
157
 
161
158
  # If a document was provided to `GraphQL::Schema#execute` instead of the raw query string, we will need to get it from the document
@@ -347,6 +344,7 @@ module GraphQL
347
344
  if @prepared_ast
348
345
  raise "Can't add filters after preparing the query"
349
346
  else
347
+ @filter ||= @schema.default_filter
350
348
  @filter = @filter.merge(only: only, except: except)
351
349
  end
352
350
  nil
@@ -361,6 +359,18 @@ module GraphQL
361
359
  schema.handle_or_reraise(context, err)
362
360
  end
363
361
 
362
+ def after_lazy(value, &block)
363
+ if !defined?(@runtime_instance)
364
+ @runtime_instance = context.namespace(:interpreter_runtime)[:runtime]
365
+ end
366
+
367
+ if @runtime_instance
368
+ @runtime_instance.minimal_after_lazy(value, &block)
369
+ else
370
+ @schema.after_lazy(value, &block)
371
+ end
372
+ end
373
+
364
374
  private
365
375
 
366
376
  def find_operation(operations, operation_name)
@@ -198,8 +198,8 @@ module GraphQL
198
198
 
199
199
  def statically_coercible?
200
200
  return @statically_coercible if defined?(@statically_coercible)
201
-
202
- @statically_coercible = !@prepare.is_a?(String) && !@prepare.is_a?(Symbol)
201
+ requires_parent_object = @prepare.is_a?(String) || @prepare.is_a?(Symbol) || @own_validators
202
+ @statically_coercible = !requires_parent_object
203
203
  end
204
204
 
205
205
  # Apply the {prepare} configuration to `value`, using methods from `obj`.
@@ -264,7 +264,7 @@ module GraphQL
264
264
 
265
265
  # If this isn't lazy, then the block returns eagerly and assigns the result here
266
266
  # If it _is_ lazy, then we write the lazy to the hash, then update it later
267
- argument_values[arg_key] = context.schema.after_lazy(coerced_value) do |resolved_coerced_value|
267
+ argument_values[arg_key] = context.query.after_lazy(coerced_value) do |resolved_coerced_value|
268
268
  owner.validate_directive_argument(self, resolved_coerced_value)
269
269
  prepared_value = begin
270
270
  prepare_value(parent_object, resolved_coerced_value, context: context)
@@ -281,7 +281,7 @@ module GraphQL
281
281
  end
282
282
 
283
283
  maybe_loaded_value = loaded_value || prepared_value
284
- context.schema.after_lazy(maybe_loaded_value) do |resolved_loaded_value|
284
+ context.query.after_lazy(maybe_loaded_value) do |resolved_loaded_value|
285
285
  # TODO code smell to access such a deeply-nested constant in a distant module
286
286
  argument_values[arg_key] = GraphQL::Execution::Interpreter::ArgumentValue.new(
287
287
  value: resolved_loaded_value,
@@ -303,7 +303,7 @@ module GraphQL
303
303
  else
304
304
  load_method_owner.public_send(arg_load_method, coerced_value)
305
305
  end
306
- context.schema.after_lazy(custom_loaded_value) do |custom_value|
306
+ context.query.after_lazy(custom_loaded_value) do |custom_value|
307
307
  if loads
308
308
  if type.list?
309
309
  loaded_values = custom_value.each_with_index.map { |custom_val, idx|
@@ -34,7 +34,7 @@ module GraphQL
34
34
  @graphql_name = graphql_name.to_s
35
35
  GraphQL::NameValidator.validate!(@graphql_name)
36
36
  @description = desc || description
37
- @value = value === NOT_CONFIGURED ? @graphql_name : value
37
+ @value = value == NOT_CONFIGURED ? @graphql_name : value
38
38
  if deprecation_reason
39
39
  self.deprecation_reason = deprecation_reason
40
40
  end
@@ -26,7 +26,7 @@ module GraphQL
26
26
  # rename some inputs to avoid conflicts inside the block
27
27
  maybe_lazy = value
28
28
  value = nil
29
- context.schema.after_lazy(maybe_lazy) do |resolved_value|
29
+ context.query.after_lazy(maybe_lazy) do |resolved_value|
30
30
  value = resolved_value
31
31
  if value.is_a? GraphQL::ExecutionError
32
32
  # This isn't even going to work because context doesn't have ast_node anymore
@@ -235,7 +235,7 @@ module GraphQL
235
235
  @name = -(camelize ? Member::BuildType.camelize(name_s) : name_s)
236
236
 
237
237
  @description = description
238
- @type = @owner_type = @own_validators = @own_directives = @own_arguments = nil # these will be prepared later if necessary
238
+ @type = @owner_type = @own_validators = @own_directives = @own_arguments = @arguments_statically_coercible = nil # these will be prepared later if necessary
239
239
 
240
240
  self.deprecation_reason = deprecation_reason
241
241
 
@@ -661,7 +661,7 @@ module GraphQL
661
661
 
662
662
  Schema::Validator.validate!(validators, application_object, query_ctx, args)
663
663
 
664
- query_ctx.schema.after_lazy(self.authorized?(application_object, args, query_ctx)) do |is_authorized|
664
+ query_ctx.query.after_lazy(self.authorized?(application_object, args, query_ctx)) do |is_authorized|
665
665
  if is_authorized
666
666
  with_extensions(object, args, query_ctx) do |obj, ruby_kwargs|
667
667
  method_args = ruby_kwargs
@@ -833,7 +833,7 @@ ERR
833
833
  extended_args = extended[:args]
834
834
  memos = extended[:memos] || EMPTY_HASH
835
835
 
836
- ctx.schema.after_lazy(value) do |resolved_value|
836
+ ctx.query.after_lazy(value) do |resolved_value|
837
837
  idx = 0
838
838
  @extensions.each do |ext|
839
839
  memo = memos[idx]
@@ -211,7 +211,7 @@ module GraphQL
211
211
 
212
212
  arguments = coerce_arguments(nil, value, ctx)
213
213
 
214
- ctx.schema.after_lazy(arguments) do |resolved_arguments|
214
+ ctx.query.after_lazy(arguments) do |resolved_arguments|
215
215
  if resolved_arguments.is_a?(GraphQL::Error)
216
216
  raise resolved_arguments
217
217
  else
@@ -51,7 +51,7 @@ module GraphQL
51
51
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
52
52
  def #{method_owner}load_#{arg_defn.keyword}(values, context = nil)
53
53
  argument = get_argument("#{arg_defn.graphql_name}")
54
- (context || self.context).schema.after_lazy(values) do |values2|
54
+ (context || self.context).query.after_lazy(values) do |values2|
55
55
  GraphQL::Execution::Lazy.all(values2.map { |value| load_application_object(argument, value, context || self.context) })
56
56
  end
57
57
  end
@@ -320,9 +320,11 @@ module GraphQL
320
320
  end
321
321
 
322
322
  def arguments_statically_coercible?
323
- return @arguments_statically_coercible if defined?(@arguments_statically_coercible)
324
-
325
- @arguments_statically_coercible = all_argument_definitions.all?(&:statically_coercible?)
323
+ if defined?(@arguments_statically_coercible) && !@arguments_statically_coercible.nil?
324
+ @arguments_statically_coercible
325
+ else
326
+ @arguments_statically_coercible = all_argument_definitions.all?(&:statically_coercible?)
327
+ end
326
328
  end
327
329
 
328
330
  module ArgumentClassAccessor
@@ -363,7 +365,7 @@ module GraphQL
363
365
  end
364
366
 
365
367
  def authorize_application_object(argument, id, context, loaded_application_object)
366
- context.schema.after_lazy(loaded_application_object) do |application_object|
368
+ context.query.after_lazy(loaded_application_object) do |application_object|
367
369
  if application_object.nil?
368
370
  err = GraphQL::LoadApplicationObjectFailedError.new(argument: argument, id: id, object: application_object)
369
371
  load_application_object_failed(err)
@@ -371,7 +373,7 @@ module GraphQL
371
373
  # Double-check that the located object is actually of this type
372
374
  # (Don't want to allow arbitrary access to objects this way)
373
375
  maybe_lazy_resolve_type = context.schema.resolve_type(argument.loads, application_object, context)
374
- context.schema.after_lazy(maybe_lazy_resolve_type) do |resolve_type_result|
376
+ context.query.after_lazy(maybe_lazy_resolve_type) do |resolve_type_result|
375
377
  if resolve_type_result.is_a?(Array) && resolve_type_result.size == 2
376
378
  application_object_type, application_object = resolve_type_result
377
379
  else
@@ -386,7 +388,7 @@ module GraphQL
386
388
  # This object was loaded successfully
387
389
  # and resolved to the right type,
388
390
  # now apply the `.authorized?` class method if there is one
389
- context.schema.after_lazy(application_object_type.authorized?(application_object, context)) do |authed|
391
+ context.query.after_lazy(application_object_type.authorized?(application_object, context)) do |authed|
390
392
  if authed
391
393
  application_object
392
394
  else
@@ -68,7 +68,7 @@ module GraphQL
68
68
  maybe_lazy_auth_val
69
69
  end
70
70
 
71
- context.schema.after_lazy(auth_val) do |is_authorized|
71
+ context.query.after_lazy(auth_val) do |is_authorized|
72
72
  if is_authorized
73
73
  self.new(object, context)
74
74
  else
@@ -57,12 +57,14 @@ module GraphQL
57
57
  query_root = Class.new(GraphQL::Schema::Object) do
58
58
  graphql_name "Root"
59
59
  field :throwaway_field, String
60
+ def self.visible?(ctx)
61
+ false
62
+ end
60
63
  end
61
64
  schema = Class.new(GraphQL::Schema) { query(query_root) }
62
65
 
63
66
  introspection_schema_ast = GraphQL::Language::DocumentFromSchemaDefinition.new(
64
67
  schema,
65
- except: ->(member, _) { member.graphql_name == "Root" },
66
68
  include_introspection_types: true,
67
69
  include_built_in_directives: true,
68
70
  ).document
@@ -60,7 +60,7 @@ module GraphQL
60
60
  super()
61
61
  end
62
62
 
63
- context.schema.after_lazy(return_value) do |return_hash|
63
+ context.query.after_lazy(return_value) do |return_hash|
64
64
  # It might be an error
65
65
  if return_hash.is_a?(Hash)
66
66
  return_hash[:client_mutation_id] = client_mutation_id
@@ -70,7 +70,7 @@ module GraphQL
70
70
  else
71
71
  ready?
72
72
  end
73
- context.schema.after_lazy(ready_val) do |is_ready, ready_early_return|
73
+ context.query.after_lazy(ready_val) do |is_ready, ready_early_return|
74
74
  if ready_early_return
75
75
  if is_ready != false
76
76
  raise "Unexpected result from #ready? (expected `true`, `false` or `[false, {...}]`): [#{is_ready.inspect}, #{ready_early_return.inspect}]"
@@ -81,7 +81,7 @@ module GraphQL
81
81
  # Then call each prepare hook, which may return a different value
82
82
  # for that argument, or may return a lazy object
83
83
  load_arguments_val = load_arguments(args)
84
- context.schema.after_lazy(load_arguments_val) do |loaded_args|
84
+ context.query.after_lazy(load_arguments_val) do |loaded_args|
85
85
  @prepared_arguments = loaded_args
86
86
  Schema::Validator.validate!(self.class.validators, object, context, loaded_args, as: @field)
87
87
  # Then call `authorized?`, which may raise or may return a lazy object
@@ -90,7 +90,7 @@ module GraphQL
90
90
  else
91
91
  authorized?
92
92
  end
93
- context.schema.after_lazy(authorized_val) do |(authorized_result, early_return)|
93
+ context.query.after_lazy(authorized_val) do |(authorized_result, early_return)|
94
94
  # If the `authorized?` returned two values, `false, early_return`,
95
95
  # then use the early return value instead of continuing
96
96
  if early_return
@@ -185,7 +185,7 @@ module GraphQL
185
185
  if arg_defn
186
186
  prepped_value = prepared_args[key] = arg_defn.load_and_authorize_value(self, value, context)
187
187
  if context.schema.lazy?(prepped_value)
188
- prepare_lazies << context.schema.after_lazy(prepped_value) do |finished_prepped_value|
188
+ prepare_lazies << context.query.after_lazy(prepped_value) do |finished_prepped_value|
189
189
  prepared_args[key] = finished_prepped_value
190
190
  end
191
191
  end
@@ -90,14 +90,20 @@ module GraphQL
90
90
  # @param filter [<#call(member)>] Objects are hidden when `.call(member, ctx)` returns true
91
91
  # @param context [GraphQL::Query::Context]
92
92
  # @param schema [GraphQL::Schema]
93
- def initialize(filter, context:, schema:)
93
+ def initialize(filter = nil, context:, schema:)
94
94
  @schema = schema
95
95
  # Cache these to avoid repeated hits to the inheritance chain when one isn't present
96
96
  @query = @schema.query
97
97
  @mutation = @schema.mutation
98
98
  @subscription = @schema.subscription
99
99
  @context = context
100
- @visibility_cache = read_through { |m| filter.call(m, context) }
100
+ @visibility_cache = if filter
101
+ read_through { |m| filter.call(m, context) }
102
+ else
103
+ read_through { |m| schema.visible?(m, context) }
104
+ end
105
+
106
+ @visibility_cache.compare_by_identity
101
107
  # Initialize all ivars to improve object shape consistency:
102
108
  @types = @visible_types = @reachable_types = @visible_parent_fields =
103
109
  @visible_possible_types = @visible_fields = @visible_arguments = @visible_enum_arrays =
@@ -244,11 +244,13 @@ module GraphQL
244
244
  end
245
245
 
246
246
  def default_filter
247
- GraphQL::Filter.new(except: default_mask, silence_deprecation_warning: true)
247
+ GraphQL::Filter.new(except: default_mask)
248
248
  end
249
249
 
250
250
  def default_mask(new_mask = nil)
251
251
  if new_mask
252
+ line = caller(2, 10).find { |l| !l.include?("lib/graphql") }
253
+ GraphQL::Deprecation.warn("GraphQL::Filter and Schema.mask are deprecated and will be removed in v2.1.0. Implement `visible?` on your schema members instead (https://graphql-ruby.org/authorization/visibility.html).\n #{line}")
252
254
  @own_default_mask = new_mask
253
255
  else
254
256
  @own_default_mask || find_inherited_value(:default_mask, Schema::NullMask)
@@ -988,9 +990,7 @@ module GraphQL
988
990
  end
989
991
 
990
992
  def new_trace(**options)
991
- if defined?(@trace_options)
992
- options = trace_options.merge(options)
993
- end
993
+ options = trace_options.merge(options)
994
994
  trace_mode = if (target = options[:query] || options[:multiplex]) && target.context[:backtrace]
995
995
  :default_backtrace
996
996
  else
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "graphql/tracing/platform_trace"
4
+
3
5
  module GraphQL
4
6
  module Tracing
5
7
  # This implementation forwards events to a notification handler (i.e.
@@ -20,6 +22,7 @@ module GraphQL
20
22
  "validate" => "validate.graphql",
21
23
  "analyze_multiplex" => "analyze_multiplex.graphql",
22
24
  "analyze_query" => "analyze_query.graphql",
25
+ "execute_multiplex" => "execute_multiplex.graphql",
23
26
  "execute_query" => "execute_query.graphql",
24
27
  "execute_query_lazy" => "execute_query_lazy.graphql",
25
28
  "execute_field" => "execute_field.graphql",
@@ -14,6 +14,7 @@ require "graphql/tracing/statsd_tracing"
14
14
  require "graphql/tracing/prometheus_tracing"
15
15
 
16
16
  # New Tracing:
17
+ require "graphql/tracing/active_support_notifications_trace"
17
18
  require "graphql/tracing/platform_trace"
18
19
  require "graphql/tracing/appoptics_trace"
19
20
  require "graphql/tracing/appsignal_trace"
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- VERSION = "2.0.21"
3
+ VERSION = "2.0.22"
4
4
  end
data/readme.md CHANGED
@@ -44,6 +44,6 @@ I also sell [GraphQL::Pro](https://graphql.pro) which provides several features
44
44
 
45
45
  ## Getting Involved
46
46
 
47
- - __Say hi & ask questions__ in the #graphql-ruby channel on [Discord](https://discord.com/invite/xud7bH9) or [on Twitter](https://twitter.com/rmosolgo)!
47
+ - __Say hi & ask questions__ in the #graphql-ruby channel on [Discord](https://discord.com/invite/xud7bH9).
48
48
  - __Report bugs__ by posting a description, full stack trace, and all relevant code in a [GitHub issue](https://github.com/rmosolgo/graphql-ruby/issues).
49
49
  - __Start hacking__ with the [Development guide](https://graphql-ruby.org/development).
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphql
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.21
4
+ version: 2.0.22
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Mosolgo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-04-11 00:00:00.000000000 Z
11
+ date: 2023-05-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: benchmark-ips
@@ -622,7 +622,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
622
622
  - !ruby/object:Gem::Version
623
623
  version: '0'
624
624
  requirements: []
625
- rubygems_version: 3.4.1
625
+ rubygems_version: 3.4.13
626
626
  signing_key:
627
627
  specification_version: 4
628
628
  summary: A GraphQL language and runtime for Ruby