graphql 2.0.0 → 2.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -1
  3. data/lib/graphql/dataloader/null_dataloader.rb +3 -1
  4. data/lib/graphql/execution/errors.rb +12 -74
  5. data/lib/graphql/execution/interpreter/runtime.rb +35 -38
  6. data/lib/graphql/query/context.rb +96 -9
  7. data/lib/graphql/query/input_validation_result.rb +10 -1
  8. data/lib/graphql/query/null_context.rb +0 -3
  9. data/lib/graphql/query.rb +2 -5
  10. data/lib/graphql/relay/range_add.rb +9 -16
  11. data/lib/graphql/schema/addition.rb +5 -0
  12. data/lib/graphql/schema/argument.rb +13 -4
  13. data/lib/graphql/schema/enum.rb +3 -5
  14. data/lib/graphql/schema/field.rb +164 -130
  15. data/lib/graphql/schema/input_object.rb +12 -12
  16. data/lib/graphql/schema/list.rb +2 -1
  17. data/lib/graphql/schema/member/has_arguments.rb +36 -6
  18. data/lib/graphql/schema/member/has_directives.rb +1 -1
  19. data/lib/graphql/schema/member/has_interfaces.rb +1 -1
  20. data/lib/graphql/schema/member/relay_shortcuts.rb +28 -2
  21. data/lib/graphql/schema/member/validates_input.rb +2 -2
  22. data/lib/graphql/schema/object.rb +14 -9
  23. data/lib/graphql/schema/relay_classic_mutation.rb +32 -14
  24. data/lib/graphql/schema/resolver/has_payload_type.rb +11 -1
  25. data/lib/graphql/schema/resolver.rb +23 -45
  26. data/lib/graphql/schema/scalar.rb +7 -7
  27. data/lib/graphql/schema/subscription.rb +0 -7
  28. data/lib/graphql/schema/warden.rb +1 -1
  29. data/lib/graphql/schema.rb +35 -6
  30. data/lib/graphql/subscriptions.rb +10 -3
  31. data/lib/graphql/tracing/data_dog_tracing.rb +3 -1
  32. data/lib/graphql/types/relay/connection_behaviors.rb +0 -16
  33. data/lib/graphql/version.rb +1 -1
  34. metadata +6 -7
  35. data/lib/graphql/query/literal_input.rb +0 -131
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c54b2619d5d111575839ba3e5d34794d993cb37a0b9aee4d41347551bb6be846
4
- data.tar.gz: f6d53678c268767478a2a86ec34c045ab8744f4ea6f97deeebed3708df634340
3
+ metadata.gz: 35d89f9a897b7897a59009d2b66b436750540da911e3c8e86e185f317d74b0e8
4
+ data.tar.gz: 13c06d0144815bbafcdff091815fa939969e51d693e1e33138358131df509783
5
5
  SHA512:
6
- metadata.gz: c7ccf09ae0a94e0cdce47f66555681391ac29af9cb544b662e8f9cd127905cda5cf5923dc64500dcab15899c7c51d5d2458ae78b95cb40ca47b1ce792e35f52c
7
- data.tar.gz: ba6d04f4fc107fe9e2daaf70bd06288f85a6ead011764ab1b975e1796e97fa8c289cc08186bbf23c6020670a585c1a48c22d2acbd28ed10c6ec09b97f07a2a47
6
+ metadata.gz: '063911bc20713d486f3dca9cec21a881a4558a300b8cf03a19f34a82f23cbcd808f3a9b0b51b886b795c3c42e6b559b954e64b6a2a3f69ddd009145ae09cee4f'
7
+ data.tar.gz: cbe4c5cda73c9fcc7bfb6a0e2249ba8c8dbad4e26ad3c2baf7cc5775fac6f7f19ac036baaea25d80b2fbaa58e9cade521a26d7b3c5bdd936b63efc9971c28ce1
@@ -1,5 +1,4 @@
1
1
  # frozen_string_literal: true
2
- require_relative "./query_complexity"
3
2
  module GraphQL
4
3
  module Analysis
5
4
  module AST
@@ -11,7 +11,9 @@ module GraphQL
11
11
  # executed sychronously.
12
12
  def run; end
13
13
  def run_isolated; yield; end
14
- def yield; end
14
+ def yield
15
+ raise GraphQL::Error, "GraphQL::Dataloader is not running -- add `use GraphQL::Dataloader` to your schema to use Dataloader sources."
16
+ end
15
17
 
16
18
  def append_job
17
19
  yield
@@ -2,51 +2,15 @@
2
2
 
3
3
  module GraphQL
4
4
  module Execution
5
- # A plugin that wraps query execution with error handling. Installed by default.
6
- #
7
- # @example Handling ActiveRecord::NotFound
8
- #
9
- # class MySchema < GraphQL::Schema
10
- # rescue_from(ActiveRecord::NotFound) do |err, obj, args, ctx, field|
11
- # ErrorTracker.log("Not Found: #{err.message}")
12
- # nil
13
- # end
14
- # end
15
- #
16
5
  class Errors
17
- NEW_HANDLER_HASH = ->(h, k) {
18
- h[k] = {
19
- class: k,
20
- handler: nil,
21
- subclass_handlers: Hash.new(&NEW_HANDLER_HASH),
22
- }
23
- }
24
-
25
- def initialize(schema)
26
- @schema = schema
27
- @handlers = {
28
- class: nil,
29
- handler: nil,
30
- subclass_handlers: Hash.new(&NEW_HANDLER_HASH),
31
- }
32
- end
33
-
34
- # @api private
35
- def each_rescue
36
- handlers = @handlers.values
37
- while (handler = handlers.shift) do
38
- yield(handler[:class], handler[:handler])
39
- handlers.concat(handler[:subclass_handlers].values)
40
- end
41
- end
42
-
43
6
  # Register this handler, updating the
44
7
  # internal handler index to maintain least-to-most specific.
45
8
  #
46
9
  # @param error_class [Class<Exception>]
10
+ # @param error_handlers [Hash]
47
11
  # @param error_handler [Proc]
48
12
  # @return [void]
49
- def rescue_from(error_class, error_handler)
13
+ def self.register_rescue_from(error_class, error_handlers, error_handler)
50
14
  subclasses_handlers = {}
51
15
  this_level_subclasses = []
52
16
  # During this traversal, do two things:
@@ -54,13 +18,12 @@ module GraphQL
54
18
  # and gather them up to be inserted _under_ this class
55
19
  # - Find the point in the index where this handler should be inserted
56
20
  # (That is, _under_ any superclasses, or at top-level, if there are no superclasses registered)
57
- handlers = @handlers[:subclass_handlers]
58
- while (handlers) do
21
+ while (error_handlers) do
59
22
  this_level_subclasses.clear
60
23
  # First, identify already-loaded handlers that belong
61
24
  # _under_ this one. (That is, they're handlers
62
25
  # for subclasses of `error_class`.)
63
- handlers.each do |err_class, handler|
26
+ error_handlers.each do |err_class, handler|
64
27
  if err_class < error_class
65
28
  subclasses_handlers[err_class] = handler
66
29
  this_level_subclasses << err_class
@@ -68,13 +31,13 @@ module GraphQL
68
31
  end
69
32
  # Any handlers that we'll be moving, delete them from this point in the index
70
33
  this_level_subclasses.each do |err_class|
71
- handlers.delete(err_class)
34
+ error_handlers.delete(err_class)
72
35
  end
73
36
 
74
37
  # See if any keys in this hash are superclasses of this new class:
75
- next_index_point = handlers.find { |err_class, handler| error_class < err_class }
38
+ next_index_point = error_handlers.find { |err_class, handler| error_class < err_class }
76
39
  if next_index_point
77
- handlers = next_index_point[1][:subclass_handlers]
40
+ error_handlers = next_index_point[1][:subclass_handlers]
78
41
  else
79
42
  # this new handler doesn't belong to any sub-handlers,
80
43
  # so insert it in the current set of `handlers`
@@ -83,40 +46,15 @@ module GraphQL
83
46
  end
84
47
  # Having found the point at which to insert this handler,
85
48
  # register it and merge any subclass handlers back in at this point.
86
- this_class_handlers = handlers[error_class]
49
+ this_class_handlers = error_handlers[error_class]
87
50
  this_class_handlers[:handler] = error_handler
88
51
  this_class_handlers[:subclass_handlers].merge!(subclasses_handlers)
89
52
  nil
90
53
  end
91
54
 
92
- # Call the given block with the schema's configured error handlers.
93
- #
94
- # If the block returns a lazy value, it's not wrapped with error handling. That area will have to be wrapped itself.
95
- #
96
- # @param ctx [GraphQL::Query::Context]
97
- # @return [Object] Either the result of the given block, or some object to replace the result, in case of error handling.
98
- def with_error_handling(ctx)
99
- yield
100
- rescue StandardError => err
101
- handler = find_handler_for(err.class)
102
- if handler
103
- runtime_info = ctx.namespace(:interpreter) || {}
104
- obj = runtime_info[:current_object]
105
- args = runtime_info[:current_arguments]
106
- args = args && args.keyword_arguments
107
- field = runtime_info[:current_field]
108
- if obj.is_a?(GraphQL::Schema::Object)
109
- obj = obj.object
110
- end
111
- handler[:handler].call(err, obj, args, ctx, field)
112
- else
113
- raise err
114
- end
115
- end
116
-
117
55
  # @return [Proc, nil] The handler for `error_class`, if one was registered on this schema or inherited
118
- def find_handler_for(error_class)
119
- handlers = @handlers[:subclass_handlers]
56
+ def self.find_handler_for(schema, error_class)
57
+ handlers = schema.error_handlers[:subclass_handlers]
120
58
  handler = nil
121
59
  while (handlers) do
122
60
  _err_class, next_handler = handlers.find { |err_class, handler| error_class <= err_class }
@@ -131,8 +69,8 @@ module GraphQL
131
69
  end
132
70
 
133
71
  # check for a handler from a parent class:
134
- if @schema.superclass.respond_to?(:error_handler) && (parent_errors = @schema.superclass.error_handler)
135
- parent_handler = parent_errors.find_handler_for(error_class)
72
+ if schema.superclass.respond_to?(:error_handlers)
73
+ parent_handler = find_handler_for(schema.superclass, error_class)
136
74
  end
137
75
 
138
76
  # If the inherited handler is more specific than the one defined here,
@@ -230,7 +230,6 @@ module GraphQL
230
230
  call_method_on_directives(:resolve, object_proxy, selections.graphql_directives) do
231
231
  evaluate_selections(
232
232
  path,
233
- context.scoped_context,
234
233
  object_proxy,
235
234
  root_type,
236
235
  root_op_type == "mutation",
@@ -349,7 +348,7 @@ module GraphQL
349
348
  NO_ARGS = {}.freeze
350
349
 
351
350
  # @return [void]
352
- def evaluate_selections(path, scoped_context, owner_object, owner_type, is_eager_selection, gathered_selections, selections_result, target_result, parent_object) # rubocop:disable Metrics/ParameterLists
351
+ def evaluate_selections(path, owner_object, owner_type, is_eager_selection, gathered_selections, selections_result, target_result, parent_object) # rubocop:disable Metrics/ParameterLists
353
352
  set_all_interpreter_context(owner_object, nil, nil, path)
354
353
 
355
354
  finished_jobs = 0
@@ -357,7 +356,7 @@ module GraphQL
357
356
  gathered_selections.each do |result_name, field_ast_nodes_or_ast_node|
358
357
  @dataloader.append_job {
359
358
  evaluate_selection(
360
- path, result_name, field_ast_nodes_or_ast_node, scoped_context, owner_object, owner_type, is_eager_selection, selections_result, parent_object
359
+ path, result_name, field_ast_nodes_or_ast_node, owner_object, owner_type, is_eager_selection, selections_result, parent_object
361
360
  )
362
361
  finished_jobs += 1
363
362
  if target_result && finished_jobs == enqueued_jobs
@@ -372,7 +371,7 @@ module GraphQL
372
371
  attr_reader :progress_path
373
372
 
374
373
  # @return [void]
375
- def evaluate_selection(path, result_name, field_ast_nodes_or_ast_node, scoped_context, owner_object, owner_type, is_eager_field, selections_result, parent_object) # rubocop:disable Metrics/ParameterLists
374
+ def evaluate_selection(path, result_name, field_ast_nodes_or_ast_node, owner_object, owner_type, is_eager_field, selections_result, parent_object) # rubocop:disable Metrics/ParameterLists
376
375
  return if dead_result?(selections_result)
377
376
  # As a performance optimization, the hash key will be a `Node` if
378
377
  # there's only one selection of the field. But if there are multiple
@@ -414,8 +413,6 @@ module GraphQL
414
413
  end
415
414
  # Set this before calling `run_with_directives`, so that the directive can have the latest path
416
415
  set_all_interpreter_context(nil, field_defn, nil, next_path)
417
-
418
- context.scoped_context = scoped_context
419
416
  object = owner_object
420
417
 
421
418
  if is_introspection
@@ -425,19 +422,18 @@ module GraphQL
425
422
  total_args_count = field_defn.arguments(context).size
426
423
  if total_args_count == 0
427
424
  resolved_arguments = GraphQL::Execution::Interpreter::Arguments::EMPTY
428
- evaluate_selection_with_args(resolved_arguments, field_defn, next_path, ast_node, field_ast_nodes, scoped_context, owner_type, object, is_eager_field, result_name, selections_result, parent_object)
425
+ evaluate_selection_with_args(resolved_arguments, field_defn, next_path, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selections_result, parent_object)
429
426
  else
430
427
  # TODO remove all arguments(...) usages?
431
428
  @query.arguments_cache.dataload_for(ast_node, field_defn, object) do |resolved_arguments|
432
- evaluate_selection_with_args(resolved_arguments, field_defn, next_path, ast_node, field_ast_nodes, scoped_context, owner_type, object, is_eager_field, result_name, selections_result, parent_object)
429
+ evaluate_selection_with_args(resolved_arguments, field_defn, next_path, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selections_result, parent_object)
433
430
  end
434
431
  end
435
432
  end
436
433
 
437
- def evaluate_selection_with_args(arguments, field_defn, next_path, ast_node, field_ast_nodes, scoped_context, owner_type, object, is_eager_field, result_name, selection_result, parent_object) # rubocop:disable Metrics/ParameterLists
438
- context.scoped_context = scoped_context
434
+ def evaluate_selection_with_args(arguments, field_defn, next_path, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selection_result, parent_object) # rubocop:disable Metrics/ParameterLists
439
435
  return_type = field_defn.type
440
- after_lazy(arguments, owner: owner_type, field: field_defn, path: next_path, ast_node: ast_node, scoped_context: context.scoped_context, owner_object: object, arguments: arguments, result_name: result_name, result: selection_result) do |resolved_arguments|
436
+ after_lazy(arguments, owner: owner_type, field: field_defn, path: next_path, ast_node: ast_node, owner_object: object, arguments: arguments, result_name: result_name, result: selection_result) do |resolved_arguments|
441
437
  if resolved_arguments.is_a?(GraphQL::ExecutionError) || resolved_arguments.is_a?(GraphQL::UnauthorizedError)
442
438
  continue_value(next_path, resolved_arguments, owner_type, field_defn, return_type.non_null?, ast_node, result_name, selection_result)
443
439
  next
@@ -502,15 +498,19 @@ module GraphQL
502
498
  field_result = call_method_on_directives(:resolve, object, directives) do
503
499
  # Actually call the field resolver and capture the result
504
500
  app_result = begin
505
- query.with_error_handling do
506
- query.trace("execute_field", {owner: owner_type, field: field_defn, path: next_path, ast_node: ast_node, query: query, object: object, arguments: kwarg_arguments}) do
507
- field_defn.resolve(object, kwarg_arguments, context)
508
- end
501
+ query.trace("execute_field", {owner: owner_type, field: field_defn, path: next_path, ast_node: ast_node, query: query, object: object, arguments: kwarg_arguments}) do
502
+ field_defn.resolve(object, kwarg_arguments, context)
509
503
  end
510
504
  rescue GraphQL::ExecutionError => err
511
505
  err
506
+ rescue StandardError => err
507
+ begin
508
+ query.handle_or_reraise(err)
509
+ rescue GraphQL::ExecutionError => ex_err
510
+ ex_err
511
+ end
512
512
  end
513
- after_lazy(app_result, owner: owner_type, field: field_defn, path: next_path, ast_node: ast_node, scoped_context: context.scoped_context, owner_object: object, arguments: resolved_arguments, result_name: result_name, result: selection_result) do |inner_result|
513
+ after_lazy(app_result, owner: owner_type, field: field_defn, path: next_path, ast_node: ast_node, owner_object: object, arguments: resolved_arguments, result_name: result_name, result: selection_result) do |inner_result|
514
514
  continue_value = continue_value(next_path, inner_result, owner_type, field_defn, return_type.non_null?, ast_node, result_name, selection_result)
515
515
  if HALT != continue_value
516
516
  continue_field(next_path, continue_value, owner_type, field_defn, return_type, ast_node, next_selections, false, object, resolved_arguments, result_name, selection_result)
@@ -688,7 +688,7 @@ module GraphQL
688
688
  resolved_type_or_lazy, resolved_value = resolve_type(current_type, value, path)
689
689
  resolved_value ||= value
690
690
 
691
- after_lazy(resolved_type_or_lazy, owner: current_type, path: path, ast_node: ast_node, scoped_context: context.scoped_context, field: field, owner_object: owner_object, arguments: arguments, trace: false, result_name: result_name, result: selection_result) do |resolved_type|
691
+ after_lazy(resolved_type_or_lazy, owner: current_type, path: path, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, trace: false, result_name: result_name, result: selection_result) do |resolved_type|
692
692
  possible_types = query.possible_types(current_type)
693
693
 
694
694
  if !possible_types.include?(resolved_type)
@@ -708,7 +708,7 @@ module GraphQL
708
708
  rescue GraphQL::ExecutionError => err
709
709
  err
710
710
  end
711
- after_lazy(object_proxy, owner: current_type, path: path, ast_node: ast_node, scoped_context: context.scoped_context, field: field, owner_object: owner_object, arguments: arguments, trace: false, result_name: result_name, result: selection_result) do |inner_object|
711
+ after_lazy(object_proxy, owner: current_type, path: path, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, trace: false, result_name: result_name, result: selection_result) do |inner_object|
712
712
  continue_value = continue_value(path, inner_object, owner_type, field, is_non_null, ast_node, result_name, selection_result)
713
713
  if HALT != continue_value
714
714
  response_hash = GraphQLResultHash.new(result_name, selection_result)
@@ -734,7 +734,6 @@ module GraphQL
734
734
  call_method_on_directives(:resolve, continue_value, selections.graphql_directives) do
735
735
  evaluate_selections(
736
736
  path,
737
- context.scoped_context,
738
737
  continue_value,
739
738
  current_type,
740
739
  false,
@@ -757,7 +756,6 @@ module GraphQL
757
756
  set_result(selection_result, result_name, response_list)
758
757
 
759
758
  idx = 0
760
- scoped_context = context.scoped_context
761
759
  begin
762
760
  value.each do |inner_value|
763
761
  break if dead_result?(response_list)
@@ -768,10 +766,10 @@ module GraphQL
768
766
  idx += 1
769
767
  if use_dataloader_job
770
768
  @dataloader.append_job do
771
- resolve_list_item(inner_value, inner_type, next_path, ast_node, scoped_context, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type)
769
+ resolve_list_item(inner_value, inner_type, next_path, ast_node, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type)
772
770
  end
773
771
  else
774
- resolve_list_item(inner_value, inner_type, next_path, ast_node, scoped_context, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type)
772
+ resolve_list_item(inner_value, inner_type, next_path, ast_node, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type)
775
773
  end
776
774
  end
777
775
  rescue NoMethodError => err
@@ -791,11 +789,11 @@ module GraphQL
791
789
  end
792
790
  end
793
791
 
794
- def resolve_list_item(inner_value, inner_type, next_path, ast_node, scoped_context, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type) # rubocop:disable Metrics/ParameterLists
792
+ def resolve_list_item(inner_value, inner_type, next_path, ast_node, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type) # rubocop:disable Metrics/ParameterLists
795
793
  set_all_interpreter_context(nil, nil, nil, next_path)
796
794
  call_method_on_directives(:resolve_each, owner_object, ast_node.directives) do
797
795
  # This will update `response_list` with the lazy
798
- after_lazy(inner_value, owner: inner_type, path: next_path, ast_node: ast_node, scoped_context: scoped_context, field: field, owner_object: owner_object, arguments: arguments, result_name: this_idx, result: response_list) do |inner_inner_value|
796
+ after_lazy(inner_value, owner: inner_type, path: next_path, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, result_name: this_idx, result: response_list) do |inner_inner_value|
799
797
  continue_value = continue_value(next_path, inner_inner_value, owner_type, field, inner_type.non_null?, ast_node, this_idx, response_list)
800
798
  if HALT != continue_value
801
799
  continue_field(next_path, continue_value, owner_type, field, inner_type, ast_node, next_selections, false, owner_object, arguments, this_idx, response_list)
@@ -870,29 +868,28 @@ module GraphQL
870
868
  # @param eager [Boolean] Set to `true` for mutation root fields only
871
869
  # @param trace [Boolean] If `false`, don't wrap this with field tracing
872
870
  # @return [GraphQL::Execution::Lazy, Object] If loading `object` will be deferred, it's a wrapper over it.
873
- def after_lazy(lazy_obj, owner:, field:, path:, scoped_context:, owner_object:, arguments:, ast_node:, result:, result_name:, eager: false, trace: true, &block)
871
+ def after_lazy(lazy_obj, owner:, field:, path:, owner_object:, arguments:, ast_node:, result:, result_name:, eager: false, trace: true, &block)
874
872
  if lazy?(lazy_obj)
875
873
  lazy = GraphQL::Execution::Lazy.new(path: path, field: field) do
876
874
  set_all_interpreter_context(owner_object, field, arguments, path)
877
- context.scoped_context = scoped_context
878
875
  # Wrap the execution of _this_ method with tracing,
879
876
  # but don't wrap the continuation below
880
877
  inner_obj = begin
881
- query.with_error_handling do
882
- begin
883
- if trace
884
- query.trace("execute_field_lazy", {owner: owner, field: field, path: path, query: query, object: owner_object, arguments: arguments, ast_node: ast_node}) do
885
- schema.sync_lazy(lazy_obj)
886
- end
887
- else
888
- schema.sync_lazy(lazy_obj)
889
- end
890
- rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => err
891
- err
878
+ if trace
879
+ query.trace("execute_field_lazy", {owner: owner, field: field, path: path, query: query, object: owner_object, arguments: arguments, ast_node: ast_node}) do
880
+ schema.sync_lazy(lazy_obj)
892
881
  end
882
+ else
883
+ schema.sync_lazy(lazy_obj)
893
884
  end
894
- rescue GraphQL::ExecutionError => ex_err
885
+ rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => ex_err
895
886
  ex_err
887
+ rescue StandardError => err
888
+ begin
889
+ query.handle_or_reraise(err)
890
+ rescue GraphQL::ExecutionError => ex_err
891
+ ex_err
892
+ end
896
893
  end
897
894
  yield(inner_obj)
898
895
  end
@@ -86,8 +86,83 @@ module GraphQL
86
86
  @errors = []
87
87
  @path = []
88
88
  @value = nil
89
- @context = self # for SharedMethods
90
- @scoped_context = {}
89
+ @context = self # for SharedMethods TODO delete sharedmethods
90
+ @scoped_context = ScopedContext.new(self)
91
+ end
92
+
93
+ class ScopedContext
94
+ def initialize(query_context)
95
+ @query_context = query_context
96
+ @path_contexts = {}
97
+ @no_path = [].freeze
98
+ end
99
+
100
+ def merged_context
101
+ merged_ctx = {}
102
+ each_present_path_ctx do |path_ctx|
103
+ merged_ctx = path_ctx.merge(merged_ctx)
104
+ end
105
+ merged_ctx
106
+ end
107
+
108
+ def merge!(hash)
109
+ current_ctx = @path_contexts[current_path] ||= {}
110
+ current_ctx.merge!(hash)
111
+ end
112
+
113
+ def current_path
114
+ @query_context.namespace(:interpreter)[:current_path] || @no_path
115
+ end
116
+
117
+ def key?(key)
118
+ each_present_path_ctx do |path_ctx|
119
+ if path_ctx.key?(key)
120
+ return true
121
+ end
122
+ end
123
+ false
124
+ end
125
+
126
+ def [](key)
127
+ each_present_path_ctx do |path_ctx|
128
+ if path_ctx.key?(key)
129
+ return path_ctx[key]
130
+ end
131
+ end
132
+ nil
133
+ end
134
+
135
+ def dig(key, *other_keys)
136
+ each_present_path_ctx do |path_ctx|
137
+ if path_ctx.key?(key)
138
+ found_value = path_ctx[key]
139
+ if other_keys.any?
140
+ return found_value.dig(*other_keys)
141
+ else
142
+ return found_value
143
+ end
144
+ end
145
+ end
146
+ nil
147
+ end
148
+
149
+ private
150
+
151
+ # Start at the current location,
152
+ # but look up the tree for previously-assigned scoped values
153
+ def each_present_path_ctx
154
+ search_path = current_path.dup
155
+ if (current_path_ctx = @path_contexts[search_path])
156
+ yield(current_path_ctx)
157
+ end
158
+
159
+ while search_path.size > 0
160
+ search_path.pop # look one level higher
161
+ if (search_path_ctx = @path_contexts[search_path])
162
+ yield(search_path_ctx)
163
+ end
164
+ end
165
+ end
91
166
  end
92
167
 
93
168
  # @return [Hash] A hash that will be added verbatim to the result hash, as `"extensions" => { ... }`
@@ -106,7 +181,7 @@ module GraphQL
106
181
  attr_writer :value
107
182
 
108
183
  # @api private
109
- attr_accessor :scoped_context
184
+ attr_reader :scoped_context
110
185
 
111
186
  def []=(key, value)
112
187
  @provided_values[key] = value
@@ -119,8 +194,11 @@ module GraphQL
119
194
 
120
195
  # Lookup `key` from the hash passed to {Schema#execute} as `context:`
121
196
  def [](key)
122
- return @scoped_context[key] if @scoped_context.key?(key)
123
- @provided_values[key]
197
+ if @scoped_context.key?(key)
198
+ @scoped_context[key]
199
+ else
200
+ @provided_values[key]
201
+ end
124
202
  end
125
203
 
126
204
  def delete(key)
@@ -135,7 +213,7 @@ module GraphQL
135
213
 
136
214
  def fetch(key, default = UNSPECIFIED_FETCH_DEFAULT)
137
215
  if @scoped_context.key?(key)
138
- @scoped_context[key]
216
+ scoped_context[key]
139
217
  elsif @provided_values.key?(key)
140
218
  @provided_values[key]
141
219
  elsif default != UNSPECIFIED_FETCH_DEFAULT
@@ -148,12 +226,21 @@ module GraphQL
148
226
  end
149
227
 
150
228
  def dig(key, *other_keys)
151
- @scoped_context.key?(key) ? @scoped_context.dig(key, *other_keys) : @provided_values.dig(key, *other_keys)
229
+ if @scoped_context.key?(key)
230
+ @scoped_context.dig(key, *other_keys)
231
+ else
232
+ @provided_values.dig(key, *other_keys)
233
+ end
152
234
  end
153
235
 
154
236
  def to_h
155
- @provided_values.merge(@scoped_context)
237
+ if (current_scoped_context = @scoped_context.merged_context)
238
+ @provided_values.merge(current_scoped_context)
239
+ else
240
+ @provided_values
241
+ end
156
242
  end
243
+
157
244
  alias :to_hash :to_h
158
245
 
159
246
  def key?(key)
@@ -185,7 +272,7 @@ module GraphQL
185
272
  end
186
273
 
187
274
  def scoped_merge!(hash)
188
- @scoped_context = @scoped_context.merge(hash)
275
+ @scoped_context.merge!(hash)
189
276
  end
190
277
 
191
278
  def scoped_set!(key, value)
@@ -4,6 +4,12 @@ module GraphQL
4
4
  class InputValidationResult
5
5
  attr_accessor :problems
6
6
 
7
+ def self.from_problem(explanation, path = nil, extensions: nil, message: nil)
8
+ result = self.new
9
+ result.add_problem(explanation, path, extensions: extensions, message: message)
10
+ result
11
+ end
12
+
7
13
  def initialize(valid: true, problems: nil)
8
14
  @valid = valid
9
15
  @problems = problems
@@ -27,7 +33,7 @@ module GraphQL
27
33
  end
28
34
 
29
35
  def merge_result!(path, inner_result)
30
- return if inner_result.valid?
36
+ return if inner_result.nil? || inner_result.valid?
31
37
 
32
38
  if inner_result.problems
33
39
  inner_result.problems.each do |p|
@@ -38,6 +44,9 @@ module GraphQL
38
44
  # It could have been explicitly set on inner_result (if it had no problems)
39
45
  @valid = false
40
46
  end
47
+
48
+ VALID = self.new
49
+ VALID.freeze
41
50
  end
42
51
  end
43
52
  end
@@ -12,9 +12,6 @@ module GraphQL
12
12
  end
13
13
 
14
14
  class NullQuery
15
- def with_error_handling
16
- yield
17
- end
18
15
  end
19
16
 
20
17
  class NullSchema < GraphQL::Schema
data/lib/graphql/query.rb CHANGED
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  require "graphql/query/context"
3
3
  require "graphql/query/fingerprint"
4
- require "graphql/query/literal_input"
5
4
  require "graphql/query/null_context"
6
5
  require "graphql/query/result"
7
6
  require "graphql/query/variables"
@@ -332,10 +331,8 @@ module GraphQL
332
331
  end
333
332
 
334
333
  # @api private
335
- def with_error_handling
336
- schema.error_handler.with_error_handling(context) do
337
- yield
338
- end
334
+ def handle_or_reraise(err)
335
+ schema.handle_or_reraise(context, err)
339
336
  end
340
337
 
341
338
  private
@@ -31,25 +31,18 @@ module GraphQL
31
31
 
32
32
  # @param collection [Object] The list of items to wrap in a connection
33
33
  # @param item [Object] The newly-added item (will be wrapped in `edge_class`)
34
+ # @param context [GraphQL::Query::Context] The surrounding `ctx`, will be passed to the connection
34
35
  # @param parent [Object] The owner of `collection`, will be passed to the connection if provided
35
- # @param context [GraphQL::Query::Context] The surrounding `ctx`, will be passed to the connection if provided (this is required for cursor encoders)
36
36
  # @param edge_class [Class] The class to wrap `item` with (defaults to the connection's edge class)
37
- def initialize(collection:, item:, parent: nil, context: nil, edge_class: nil)
38
- if context && context.schema.new_connections?
39
- conn_class = context.schema.connections.wrapper_for(collection)
40
- # The rest will be added by ConnectionExtension
41
- @connection = conn_class.new(collection, parent: parent, context: context, edge_class: edge_class)
42
- # Check if this connection supports it, to support old versions of GraphQL-Pro
43
- @edge = if @connection.respond_to?(:range_add_edge)
44
- @connection.range_add_edge(item)
45
- else
46
- @connection.edge_class.new(item, @connection)
47
- end
37
+ def initialize(collection:, item:, context:, parent: nil, edge_class: nil)
38
+ conn_class = context.schema.connections.wrapper_for(collection)
39
+ # The rest will be added by ConnectionExtension
40
+ @connection = conn_class.new(collection, parent: parent, context: context, edge_class: edge_class)
41
+ # Check if this connection supports it, to support old versions of GraphQL-Pro
42
+ @edge = if @connection.respond_to?(:range_add_edge)
43
+ @connection.range_add_edge(item)
48
44
  else
49
- connection_class = BaseConnection.connection_for_nodes(collection)
50
- @connection = connection_class.new(collection, {}, parent: parent, context: context)
51
- edge_class ||= Relay::Edge
52
- @edge = edge_class.new(item, @connection)
45
+ @connection.edge_class.new(item, @connection)
53
46
  end
54
47
 
55
48
  @parent = parent
@@ -107,6 +107,11 @@ module GraphQL
107
107
  if !pt.include?(owner) && owner.is_a?(Class)
108
108
  pt << owner
109
109
  end
110
+ int.interfaces.each do |indirect_int|
111
+ if indirect_int.is_a?(LateBoundType) && (indirect_int_type = get_type(indirect_int.graphql_name))
112
+ update_type_owner(owner, indirect_int_type)
113
+ end
114
+ end
110
115
  end
111
116
  end
112
117
  when nil