graphql 2.0.2 → 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/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: []
|