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.
- checksums.yaml +4 -4
- data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -1
- data/lib/graphql/dataloader/null_dataloader.rb +3 -1
- data/lib/graphql/execution/errors.rb +12 -74
- data/lib/graphql/execution/interpreter/runtime.rb +35 -38
- data/lib/graphql/query/context.rb +96 -9
- data/lib/graphql/query/input_validation_result.rb +10 -1
- data/lib/graphql/query/null_context.rb +0 -3
- data/lib/graphql/query.rb +2 -5
- data/lib/graphql/relay/range_add.rb +9 -16
- data/lib/graphql/schema/addition.rb +5 -0
- data/lib/graphql/schema/argument.rb +13 -4
- data/lib/graphql/schema/enum.rb +3 -5
- data/lib/graphql/schema/field.rb +164 -130
- data/lib/graphql/schema/input_object.rb +12 -12
- data/lib/graphql/schema/list.rb +2 -1
- data/lib/graphql/schema/member/has_arguments.rb +36 -6
- data/lib/graphql/schema/member/has_directives.rb +1 -1
- data/lib/graphql/schema/member/has_interfaces.rb +1 -1
- data/lib/graphql/schema/member/relay_shortcuts.rb +28 -2
- data/lib/graphql/schema/member/validates_input.rb +2 -2
- data/lib/graphql/schema/object.rb +14 -9
- data/lib/graphql/schema/relay_classic_mutation.rb +32 -14
- data/lib/graphql/schema/resolver/has_payload_type.rb +11 -1
- data/lib/graphql/schema/resolver.rb +23 -45
- data/lib/graphql/schema/scalar.rb +7 -7
- data/lib/graphql/schema/subscription.rb +0 -7
- data/lib/graphql/schema/warden.rb +1 -1
- data/lib/graphql/schema.rb +35 -6
- data/lib/graphql/subscriptions.rb +10 -3
- data/lib/graphql/tracing/data_dog_tracing.rb +3 -1
- data/lib/graphql/types/relay/connection_behaviors.rb +0 -16
- data/lib/graphql/version.rb +1 -1
- metadata +6 -7
- data/lib/graphql/query/literal_input.rb +0 -131
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 35d89f9a897b7897a59009d2b66b436750540da911e3c8e86e185f317d74b0e8
|
4
|
+
data.tar.gz: 13c06d0144815bbafcdff091815fa939969e51d693e1e33138358131df509783
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '063911bc20713d486f3dca9cec21a881a4558a300b8cf03a19f34a82f23cbcd808f3a9b0b51b886b795c3c42e6b559b954e64b6a2a3f69ddd009145ae09cee4f'
|
7
|
+
data.tar.gz: cbe4c5cda73c9fcc7bfb6a0e2249ba8c8dbad4e26ad3c2baf7cc5775fac6f7f19ac036baaea25d80b2fbaa58e9cade521a26d7b3c5bdd936b63efc9971c28ce1
|
@@ -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
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
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 =
|
38
|
+
next_index_point = error_handlers.find { |err_class, handler| error_class < err_class }
|
76
39
|
if next_index_point
|
77
|
-
|
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 =
|
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 =
|
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
|
135
|
-
parent_handler =
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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.
|
506
|
-
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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:,
|
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
|
-
|
882
|
-
|
883
|
-
|
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
|
-
|
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
|
-
|
123
|
-
|
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
|
-
|
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)
|
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
|
-
|
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
|
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
|
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
|
336
|
-
schema.
|
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,
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
@
|
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
|
-
|
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
|