graphql 2.0.21 → 2.0.22

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