graphql 2.0.2 → 2.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/graphql/execution/errors.rb +12 -74
- data/lib/graphql/execution/interpreter/runtime.rb +20 -16
- data/lib/graphql/query/null_context.rb +0 -3
- data/lib/graphql/query.rb +2 -4
- data/lib/graphql/relay/range_add.rb +9 -16
- data/lib/graphql/schema/addition.rb +5 -0
- data/lib/graphql/schema/argument.rb +9 -3
- data/lib/graphql/schema/field.rb +108 -95
- data/lib/graphql/schema/member/has_directives.rb +1 -1
- data/lib/graphql/schema/member/relay_shortcuts.rb +28 -2
- data/lib/graphql/schema/object.rb +6 -6
- data/lib/graphql/schema/scalar.rb +3 -3
- data/lib/graphql/schema.rb +35 -6
- 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 -6
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
|
@@ -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,
|
@@ -498,13 +498,17 @@ module GraphQL
|
|
498
498
|
field_result = call_method_on_directives(:resolve, object, directives) do
|
499
499
|
# Actually call the field resolver and capture the result
|
500
500
|
app_result = begin
|
501
|
-
query.
|
502
|
-
|
503
|
-
field_defn.resolve(object, kwarg_arguments, context)
|
504
|
-
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)
|
505
503
|
end
|
506
504
|
rescue GraphQL::ExecutionError => err
|
507
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
|
508
512
|
end
|
509
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|
|
510
514
|
continue_value = continue_value(next_path, inner_result, owner_type, field_defn, return_type.non_null?, ast_node, result_name, selection_result)
|
@@ -871,21 +875,21 @@ module GraphQL
|
|
871
875
|
# Wrap the execution of _this_ method with tracing,
|
872
876
|
# but don't wrap the continuation below
|
873
877
|
inner_obj = begin
|
874
|
-
|
875
|
-
|
876
|
-
|
877
|
-
query.trace("execute_field_lazy", {owner: owner, field: field, path: path, query: query, object: owner_object, arguments: arguments, ast_node: ast_node}) do
|
878
|
-
schema.sync_lazy(lazy_obj)
|
879
|
-
end
|
880
|
-
else
|
881
|
-
schema.sync_lazy(lazy_obj)
|
882
|
-
end
|
883
|
-
rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => err
|
884
|
-
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)
|
885
881
|
end
|
882
|
+
else
|
883
|
+
schema.sync_lazy(lazy_obj)
|
886
884
|
end
|
887
|
-
rescue GraphQL::ExecutionError => ex_err
|
885
|
+
rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => ex_err
|
888
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
|
889
893
|
end
|
890
894
|
yield(inner_obj)
|
891
895
|
end
|
data/lib/graphql/query.rb
CHANGED
@@ -331,10 +331,8 @@ module GraphQL
|
|
331
331
|
end
|
332
332
|
|
333
333
|
# @api private
|
334
|
-
def
|
335
|
-
schema.
|
336
|
-
yield
|
337
|
-
end
|
334
|
+
def handle_or_reraise(err)
|
335
|
+
schema.handle_or_reraise(context, err)
|
338
336
|
end
|
339
337
|
|
340
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
|
@@ -250,24 +250,30 @@ module GraphQL
|
|
250
250
|
end
|
251
251
|
|
252
252
|
loaded_value = nil
|
253
|
-
coerced_value =
|
253
|
+
coerced_value = begin
|
254
254
|
type.coerce_input(value, context)
|
255
|
+
rescue StandardError => err
|
256
|
+
context.schema.handle_or_reraise(context, err)
|
255
257
|
end
|
256
258
|
|
257
259
|
# If this isn't lazy, then the block returns eagerly and assigns the result here
|
258
260
|
# If it _is_ lazy, then we write the lazy to the hash, then update it later
|
259
261
|
argument_values[arg_key] = context.schema.after_lazy(coerced_value) do |resolved_coerced_value|
|
260
262
|
if loads && !from_resolver?
|
261
|
-
loaded_value =
|
263
|
+
loaded_value = begin
|
262
264
|
load_and_authorize_value(owner, coerced_value, context)
|
265
|
+
rescue StandardError => err
|
266
|
+
context.schema.handle_or_reraise(context, err)
|
263
267
|
end
|
264
268
|
end
|
265
269
|
|
266
270
|
maybe_loaded_value = loaded_value || resolved_coerced_value
|
267
271
|
context.schema.after_lazy(maybe_loaded_value) do |resolved_loaded_value|
|
268
272
|
owner.validate_directive_argument(self, resolved_loaded_value)
|
269
|
-
prepared_value =
|
273
|
+
prepared_value = begin
|
270
274
|
prepare_value(parent_object, resolved_loaded_value, context: context)
|
275
|
+
rescue StandardError => err
|
276
|
+
context.schema.handle_or_reraise(context, err)
|
271
277
|
end
|
272
278
|
|
273
279
|
# TODO code smell to access such a deeply-nested constant in a distant module
|
data/lib/graphql/schema/field.rb
CHANGED
@@ -145,8 +145,18 @@ module GraphQL
|
|
145
145
|
if !@scope.nil?
|
146
146
|
# The default was overridden
|
147
147
|
@scope
|
148
|
+
elsif @return_type_expr
|
149
|
+
# Detect a list return type, but don't call `type` since that may eager-load an otherwise lazy-loaded type
|
150
|
+
@return_type_expr.is_a?(Array) ||
|
151
|
+
(@return_type_expr.is_a?(String) && @return_type_expr.include?("[")) ||
|
152
|
+
connection?
|
153
|
+
elsif @resolver_class
|
154
|
+
resolver_type = @resolver_class.type_expr
|
155
|
+
resolver_type.is_a?(Array) ||
|
156
|
+
(resolver_type.is_a?(String) && resolver_type.include?("[")) ||
|
157
|
+
connection?
|
148
158
|
else
|
149
|
-
|
159
|
+
false
|
150
160
|
end
|
151
161
|
end
|
152
162
|
|
@@ -201,7 +211,7 @@ module GraphQL
|
|
201
211
|
# @param ast_node [Language::Nodes::FieldDefinition, nil] If this schema was parsed from definition, this AST node defined the field
|
202
212
|
# @param method_conflict_warning [Boolean] If false, skip the warning if this field's method conflicts with a built-in method
|
203
213
|
# @param validates [Array<Hash>] Configurations for validating this field
|
204
|
-
def initialize(type: nil, name: nil, owner: nil, null: true, description:
|
214
|
+
def initialize(type: nil, name: nil, owner: nil, null: true, description: :not_given, deprecation_reason: nil, method: nil, hash_key: nil, dig: nil, resolver_method: nil, connection: nil, max_page_size: :not_given, scope: nil, introspection: false, camelize: true, trace: nil, complexity: nil, ast_node: nil, extras: EMPTY_ARRAY, extensions: EMPTY_ARRAY, connection_extension: self.class.connection_extension, resolver_class: nil, subscription_scope: nil, relay_node_field: false, relay_nodes_field: false, method_conflict_warning: true, broadcastable: nil, arguments: EMPTY_HASH, directives: EMPTY_HASH, validates: EMPTY_ARRAY, &definition_block)
|
205
215
|
if name.nil?
|
206
216
|
raise ArgumentError, "missing first `name` argument or keyword `name:`"
|
207
217
|
end
|
@@ -214,7 +224,9 @@ module GraphQL
|
|
214
224
|
name_s = -name.to_s
|
215
225
|
@underscored_name = -Member::BuildType.underscore(name_s)
|
216
226
|
@name = -(camelize ? Member::BuildType.camelize(name_s) : name_s)
|
217
|
-
|
227
|
+
if description != :not_given
|
228
|
+
@description = description
|
229
|
+
end
|
218
230
|
self.deprecation_reason = deprecation_reason
|
219
231
|
|
220
232
|
if method && hash_key && dig
|
@@ -337,10 +349,12 @@ module GraphQL
|
|
337
349
|
def description(text = nil)
|
338
350
|
if text
|
339
351
|
@description = text
|
352
|
+
elsif defined?(@description)
|
353
|
+
@description
|
340
354
|
elsif @resolver_class
|
341
355
|
@description || @resolver_class.description
|
342
356
|
else
|
343
|
-
|
357
|
+
nil
|
344
358
|
end
|
345
359
|
end
|
346
360
|
|
@@ -516,19 +530,19 @@ module GraphQL
|
|
516
530
|
attr_writer :type
|
517
531
|
|
518
532
|
def type
|
519
|
-
if @
|
520
|
-
t
|
521
|
-
|
522
|
-
|
533
|
+
if @return_type_expr.nil?
|
534
|
+
if @resolver_class && (t = @resolver_class.type)
|
535
|
+
t
|
536
|
+
else
|
523
537
|
# Not enough info to determine type
|
524
538
|
message = "Can't determine the return type for #{self.path}"
|
525
539
|
if @resolver_class
|
526
540
|
message += " (it has `resolver: #{@resolver_class}`, perhaps that class is missing a `type ...` declaration, or perhaps its type causes a cyclical loading issue)"
|
527
541
|
end
|
528
542
|
raise MissingReturnTypeError, message
|
529
|
-
else
|
530
|
-
Member::BuildType.parse_type(@return_type_expr, null: @return_type_null)
|
531
543
|
end
|
544
|
+
else
|
545
|
+
@type ||= Member::BuildType.parse_type(@return_type_expr, null: @return_type_null)
|
532
546
|
end
|
533
547
|
rescue GraphQL::Schema::InvalidDocumentError, MissingReturnTypeError => err
|
534
548
|
# Let this propagate up
|
@@ -599,31 +613,94 @@ module GraphQL
|
|
599
613
|
# @param object [GraphQL::Schema::Object] An instance of some type class, wrapping an application object
|
600
614
|
# @param args [Hash] A symbol-keyed hash of Ruby keyword arguments. (Empty if no args)
|
601
615
|
# @param ctx [GraphQL::Query::Context]
|
602
|
-
def resolve(object, args,
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
616
|
+
def resolve(object, args, query_ctx)
|
617
|
+
# Unwrap the GraphQL object to get the application object.
|
618
|
+
application_object = object.object
|
619
|
+
method_receiver = nil
|
620
|
+
method_to_call = nil
|
621
|
+
method_args = nil
|
622
|
+
|
623
|
+
Schema::Validator.validate!(validators, application_object, query_ctx, args)
|
624
|
+
|
625
|
+
query_ctx.schema.after_lazy(self.authorized?(application_object, args, query_ctx)) do |is_authorized|
|
626
|
+
if is_authorized
|
627
|
+
with_extensions(object, args, query_ctx) do |obj, ruby_kwargs|
|
628
|
+
method_args = ruby_kwargs
|
629
|
+
if @resolver_class
|
630
|
+
if obj.is_a?(GraphQL::Schema::Object)
|
631
|
+
obj = obj.object
|
632
|
+
end
|
633
|
+
obj = @resolver_class.new(object: obj, context: query_ctx, field: self)
|
634
|
+
end
|
635
|
+
|
636
|
+
# Find a way to resolve this field, checking:
|
637
|
+
#
|
638
|
+
# - A method on the type instance;
|
639
|
+
# - Hash keys, if the wrapped object is a hash;
|
640
|
+
# - A method on the wrapped object;
|
641
|
+
# - Or, raise not implemented.
|
642
|
+
#
|
643
|
+
if obj.respond_to?(resolver_method)
|
644
|
+
method_to_call = resolver_method
|
645
|
+
method_receiver = obj
|
646
|
+
# Call the method with kwargs, if there are any
|
647
|
+
if ruby_kwargs.any?
|
648
|
+
obj.public_send(resolver_method, **ruby_kwargs)
|
649
|
+
else
|
650
|
+
obj.public_send(resolver_method)
|
651
|
+
end
|
652
|
+
elsif obj.object.is_a?(Hash)
|
653
|
+
inner_object = obj.object
|
654
|
+
if @dig_keys
|
655
|
+
inner_object.dig(*@dig_keys)
|
656
|
+
elsif inner_object.key?(@method_sym)
|
657
|
+
inner_object[@method_sym]
|
658
|
+
else
|
659
|
+
inner_object[@method_str]
|
660
|
+
end
|
661
|
+
elsif obj.object.respond_to?(@method_sym)
|
662
|
+
method_to_call = @method_sym
|
663
|
+
method_receiver = obj.object
|
664
|
+
if ruby_kwargs.any?
|
665
|
+
obj.object.public_send(@method_sym, **ruby_kwargs)
|
666
|
+
else
|
667
|
+
obj.object.public_send(@method_sym)
|
668
|
+
end
|
669
|
+
else
|
670
|
+
raise <<-ERR
|
671
|
+
Failed to implement #{@owner.graphql_name}.#{@name}, tried:
|
609
672
|
|
610
|
-
|
673
|
+
- `#{obj.class}##{resolver_method}`, which did not exist
|
674
|
+
- `#{obj.object.class}##{@method_sym}`, which did not exist
|
675
|
+
- Looking up hash key `#{@method_sym.inspect}` or `#{@method_str.inspect}` on `#{obj.object}`, but it wasn't a Hash
|
611
676
|
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
else
|
616
|
-
raise GraphQL::UnauthorizedFieldError.new(object: application_object, type: object.class, context: ctx, field: self)
|
677
|
+
To implement this field, define one of the methods above (and check for typos)
|
678
|
+
ERR
|
679
|
+
end
|
617
680
|
end
|
681
|
+
else
|
682
|
+
raise GraphQL::UnauthorizedFieldError.new(object: application_object, type: object.class, context: query_ctx, field: self)
|
618
683
|
end
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
684
|
+
end
|
685
|
+
rescue GraphQL::UnauthorizedFieldError => err
|
686
|
+
err.field ||= self
|
687
|
+
begin
|
688
|
+
query_ctx.schema.unauthorized_field(err)
|
689
|
+
rescue GraphQL::ExecutionError => err
|
690
|
+
err
|
691
|
+
end
|
692
|
+
rescue GraphQL::UnauthorizedError => err
|
693
|
+
begin
|
694
|
+
query_ctx.schema.unauthorized_object(err)
|
695
|
+
rescue GraphQL::ExecutionError => err
|
696
|
+
err
|
697
|
+
end
|
698
|
+
rescue ArgumentError
|
699
|
+
if method_receiver && method_to_call
|
700
|
+
assert_satisfactory_implementation(method_receiver, method_to_call, method_args)
|
701
|
+
end
|
702
|
+
# if the line above doesn't raise, re-raise
|
703
|
+
raise
|
627
704
|
end
|
628
705
|
|
629
706
|
# @param ctx [GraphQL::Query::Context]
|
@@ -639,70 +716,6 @@ module GraphQL
|
|
639
716
|
|
640
717
|
private
|
641
718
|
|
642
|
-
def public_send_field(unextended_obj, unextended_ruby_kwargs, query_ctx)
|
643
|
-
with_extensions(unextended_obj, unextended_ruby_kwargs, query_ctx) do |obj, ruby_kwargs|
|
644
|
-
begin
|
645
|
-
method_receiver = nil
|
646
|
-
method_to_call = nil
|
647
|
-
if @resolver_class
|
648
|
-
if obj.is_a?(GraphQL::Schema::Object)
|
649
|
-
obj = obj.object
|
650
|
-
end
|
651
|
-
obj = @resolver_class.new(object: obj, context: query_ctx, field: self)
|
652
|
-
end
|
653
|
-
|
654
|
-
# Find a way to resolve this field, checking:
|
655
|
-
#
|
656
|
-
# - A method on the type instance;
|
657
|
-
# - Hash keys, if the wrapped object is a hash;
|
658
|
-
# - A method on the wrapped object;
|
659
|
-
# - Or, raise not implemented.
|
660
|
-
#
|
661
|
-
if obj.respond_to?(resolver_method)
|
662
|
-
method_to_call = resolver_method
|
663
|
-
method_receiver = obj
|
664
|
-
# Call the method with kwargs, if there are any
|
665
|
-
if ruby_kwargs.any?
|
666
|
-
obj.public_send(resolver_method, **ruby_kwargs)
|
667
|
-
else
|
668
|
-
obj.public_send(resolver_method)
|
669
|
-
end
|
670
|
-
elsif obj.object.is_a?(Hash)
|
671
|
-
inner_object = obj.object
|
672
|
-
if @dig_keys
|
673
|
-
inner_object.dig(*@dig_keys)
|
674
|
-
elsif inner_object.key?(@method_sym)
|
675
|
-
inner_object[@method_sym]
|
676
|
-
else
|
677
|
-
inner_object[@method_str]
|
678
|
-
end
|
679
|
-
elsif obj.object.respond_to?(@method_sym)
|
680
|
-
method_to_call = @method_sym
|
681
|
-
method_receiver = obj.object
|
682
|
-
if ruby_kwargs.any?
|
683
|
-
obj.object.public_send(@method_sym, **ruby_kwargs)
|
684
|
-
else
|
685
|
-
obj.object.public_send(@method_sym)
|
686
|
-
end
|
687
|
-
else
|
688
|
-
raise <<-ERR
|
689
|
-
Failed to implement #{@owner.graphql_name}.#{@name}, tried:
|
690
|
-
|
691
|
-
- `#{obj.class}##{resolver_method}`, which did not exist
|
692
|
-
- `#{obj.object.class}##{@method_sym}`, which did not exist
|
693
|
-
- Looking up hash key `#{@method_sym.inspect}` or `#{@method_str.inspect}` on `#{obj.object}`, but it wasn't a Hash
|
694
|
-
|
695
|
-
To implement this field, define one of the methods above (and check for typos)
|
696
|
-
ERR
|
697
|
-
end
|
698
|
-
rescue ArgumentError
|
699
|
-
assert_satisfactory_implementation(method_receiver, method_to_call, ruby_kwargs)
|
700
|
-
# if the line above doesn't raise, re-raise
|
701
|
-
raise
|
702
|
-
end
|
703
|
-
end
|
704
|
-
end
|
705
|
-
|
706
719
|
def assert_satisfactory_implementation(receiver, method_name, ruby_kwargs)
|
707
720
|
method_defn = receiver.method(method_name)
|
708
721
|
unsatisfied_ruby_kwargs = ruby_kwargs.dup
|
@@ -8,7 +8,15 @@ module GraphQL
|
|
8
8
|
if new_edge_type_class
|
9
9
|
@edge_type_class = new_edge_type_class
|
10
10
|
else
|
11
|
-
|
11
|
+
# Don't call `ancestor.edge_type_class`
|
12
|
+
# because we don't want a fallback from any ancestors --
|
13
|
+
# only apply the fallback if _no_ ancestor has a configured value!
|
14
|
+
for ancestor in self.ancestors
|
15
|
+
if ancestor.respond_to?(:configured_edge_type_class, true) && (etc = ancestor.configured_edge_type_class)
|
16
|
+
return etc
|
17
|
+
end
|
18
|
+
end
|
19
|
+
Types::Relay::BaseEdge
|
12
20
|
end
|
13
21
|
end
|
14
22
|
|
@@ -16,7 +24,15 @@ module GraphQL
|
|
16
24
|
if new_connection_type_class
|
17
25
|
@connection_type_class = new_connection_type_class
|
18
26
|
else
|
19
|
-
|
27
|
+
# Don't call `ancestor.connection_type_class`
|
28
|
+
# because we don't want a fallback from any ancestors --
|
29
|
+
# only apply the fallback if _no_ ancestor has a configured value!
|
30
|
+
for ancestor in self.ancestors
|
31
|
+
if ancestor.respond_to?(:configured_connection_type_class, true) && (ctc = ancestor.configured_connection_type_class)
|
32
|
+
return ctc
|
33
|
+
end
|
34
|
+
end
|
35
|
+
Types::Relay::BaseConnection
|
20
36
|
end
|
21
37
|
end
|
22
38
|
|
@@ -41,6 +57,16 @@ module GraphQL
|
|
41
57
|
end
|
42
58
|
end
|
43
59
|
end
|
60
|
+
|
61
|
+
protected
|
62
|
+
|
63
|
+
def configured_connection_type_class
|
64
|
+
@connection_type_class
|
65
|
+
end
|
66
|
+
|
67
|
+
def configured_edge_type_class
|
68
|
+
@edge_type_class
|
69
|
+
end
|
44
70
|
end
|
45
71
|
end
|
46
72
|
end
|
@@ -51,12 +51,12 @@ module GraphQL
|
|
51
51
|
trace_payload = { context: context, type: self, object: object, path: context[:current_path] }
|
52
52
|
|
53
53
|
maybe_lazy_auth_val = context.query.trace("authorized", trace_payload) do
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
54
|
+
begin
|
55
|
+
authorized?(object, context)
|
56
|
+
rescue GraphQL::UnauthorizedError => err
|
57
|
+
context.schema.unauthorized_object(err)
|
58
|
+
rescue StandardError => err
|
59
|
+
context.query.handle_or_reraise(err)
|
60
60
|
end
|
61
61
|
end
|
62
62
|
|
@@ -42,11 +42,11 @@ module GraphQL
|
|
42
42
|
|
43
43
|
def validate_non_null_input(value, ctx)
|
44
44
|
coerced_result = begin
|
45
|
-
ctx
|
46
|
-
coerce_input(value, ctx)
|
47
|
-
end
|
45
|
+
coerce_input(value, ctx)
|
48
46
|
rescue GraphQL::CoercionError => err
|
49
47
|
err
|
48
|
+
rescue StandardError => err
|
49
|
+
ctx.query.handle_or_reraise(err)
|
50
50
|
end
|
51
51
|
|
52
52
|
if coerced_result.nil?
|
data/lib/graphql/schema.rb
CHANGED
@@ -693,7 +693,41 @@ module GraphQL
|
|
693
693
|
|
694
694
|
def rescue_from(*err_classes, &handler_block)
|
695
695
|
err_classes.each do |err_class|
|
696
|
-
|
696
|
+
Execution::Errors.register_rescue_from(err_class, error_handlers[:subclass_handlers], handler_block)
|
697
|
+
end
|
698
|
+
end
|
699
|
+
|
700
|
+
NEW_HANDLER_HASH = ->(h, k) {
|
701
|
+
h[k] = {
|
702
|
+
class: k,
|
703
|
+
handler: nil,
|
704
|
+
subclass_handlers: Hash.new(&NEW_HANDLER_HASH),
|
705
|
+
}
|
706
|
+
}
|
707
|
+
|
708
|
+
def error_handlers
|
709
|
+
@error_handlers ||= {
|
710
|
+
class: nil,
|
711
|
+
handler: nil,
|
712
|
+
subclass_handlers: Hash.new(&NEW_HANDLER_HASH),
|
713
|
+
}
|
714
|
+
end
|
715
|
+
|
716
|
+
# @api private
|
717
|
+
def handle_or_reraise(context, err)
|
718
|
+
handler = Execution::Errors.find_handler_for(self, err.class)
|
719
|
+
if handler
|
720
|
+
runtime_info = context.namespace(:interpreter) || {}
|
721
|
+
obj = runtime_info[:current_object]
|
722
|
+
args = runtime_info[:current_arguments]
|
723
|
+
args = args && args.keyword_arguments
|
724
|
+
field = runtime_info[:current_field]
|
725
|
+
if obj.is_a?(GraphQL::Schema::Object)
|
726
|
+
obj = obj.object
|
727
|
+
end
|
728
|
+
handler[:handler].call(err, obj, args, context, field)
|
729
|
+
else
|
730
|
+
raise err
|
697
731
|
end
|
698
732
|
end
|
699
733
|
|
@@ -821,11 +855,6 @@ module GraphQL
|
|
821
855
|
ctx.errors.push(parse_err)
|
822
856
|
end
|
823
857
|
|
824
|
-
# @return [GraphQL::Execution::Errors]
|
825
|
-
def error_handler
|
826
|
-
@error_handler ||= GraphQL::Execution::Errors.new(self)
|
827
|
-
end
|
828
|
-
|
829
858
|
def lazy_resolve(lazy_class, value_method)
|
830
859
|
lazy_methods.set(lazy_class, value_method)
|
831
860
|
end
|
@@ -148,22 +148,6 @@ module GraphQL
|
|
148
148
|
obj_type.field :page_info, GraphQL::Types::Relay::PageInfo, null: false, description: "Information to aid in pagination."
|
149
149
|
end
|
150
150
|
end
|
151
|
-
|
152
|
-
# By default this calls through to the ConnectionWrapper's edge nodes method,
|
153
|
-
# but sometimes you need to override it to support the `nodes` field
|
154
|
-
def nodes
|
155
|
-
@object.edge_nodes
|
156
|
-
end
|
157
|
-
|
158
|
-
def edges
|
159
|
-
if @object.is_a?(GraphQL::Pagination::Connection)
|
160
|
-
@object.edges
|
161
|
-
else
|
162
|
-
context.schema.after_lazy(object.edge_nodes) do |nodes|
|
163
|
-
nodes.map { |n| self.class.edge_class.new(n, object) }
|
164
|
-
end
|
165
|
-
end
|
166
|
-
end
|
167
151
|
end
|
168
152
|
end
|
169
153
|
end
|
data/lib/graphql/version.rb
CHANGED
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.
|
4
|
+
version: 2.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Mosolgo
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-03-
|
11
|
+
date: 2022-03-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: benchmark-ips
|
@@ -581,7 +581,7 @@ metadata:
|
|
581
581
|
source_code_uri: https://github.com/rmosolgo/graphql-ruby
|
582
582
|
bug_tracker_uri: https://github.com/rmosolgo/graphql-ruby/issues
|
583
583
|
mailing_list_uri: https://tinyletter.com/graphql-ruby
|
584
|
-
post_install_message:
|
584
|
+
post_install_message:
|
585
585
|
rdoc_options: []
|
586
586
|
require_paths:
|
587
587
|
- lib
|
@@ -596,8 +596,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
596
596
|
- !ruby/object:Gem::Version
|
597
597
|
version: '0'
|
598
598
|
requirements: []
|
599
|
-
rubygems_version: 3.
|
600
|
-
signing_key:
|
599
|
+
rubygems_version: 3.3.3
|
600
|
+
signing_key:
|
601
601
|
specification_version: 4
|
602
602
|
summary: A GraphQL language and runtime for Ruby
|
603
603
|
test_files: []
|