graphql 2.0.0 → 2.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of graphql might be problematic. Click here for more details.

Files changed (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