graphql 2.0.2 → 2.0.5
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 +118 -103
- data/lib/graphql/schema/member/has_arguments.rb +13 -1
- 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/relay_classic_mutation.rb +4 -0
- data/lib/graphql/schema/resolver.rb +2 -2
- 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: 248d39942011f4175ee77643a6e8d103d17d60ac73ee95418eb25e0eee2bbb27
|
4
|
+
data.tar.gz: 062c4d1402adf7f3612d888c6e00df91623340239996b6bc14db59ebbec41814
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 87813bc8354e52215099b99f932b615856337649897313982e7945b9f039625fd4a788a1edba62faf4f6ee8438d87feafe39b374504561726fd7b9b5f5959d0d
|
7
|
+
data.tar.gz: d58627c8387a891814bf5a362a23035f9c58f2b5959fe3857afdec66cac7a2944601cfa03311c3791cc18038fd4a1c4faf5dd1793c54b9bbab1d2bd487e4b140
|
@@ -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
|
|
@@ -177,7 +187,7 @@ module GraphQL
|
|
177
187
|
# @param name [Symbol] The underscore-cased version of this field name (will be camelized for the GraphQL API)
|
178
188
|
# @param type [Class, GraphQL::BaseType, Array] The return type of this field
|
179
189
|
# @param owner [Class] The type that this field belongs to
|
180
|
-
# @param null [Boolean] `true` if this field may return `null`, `false` if it is never `null`
|
190
|
+
# @param null [Boolean] (defaults to `true`) `true` if this field may return `null`, `false` if it is never `null`
|
181
191
|
# @param description [String] Field description
|
182
192
|
# @param deprecation_reason [String] If present, the field is marked "deprecated" with this message
|
183
193
|
# @param method [Symbol] The method to call on the underlying object to resolve this field (defaults to `name`)
|
@@ -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:
|
214
|
+
def initialize(type: nil, name: nil, owner: nil, null: nil, 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
|
@@ -241,7 +253,13 @@ module GraphQL
|
|
241
253
|
@resolver_method = resolver_method
|
242
254
|
@complexity = complexity
|
243
255
|
@return_type_expr = type
|
244
|
-
@return_type_null = null
|
256
|
+
@return_type_null = if !null.nil?
|
257
|
+
null
|
258
|
+
elsif resolver_class
|
259
|
+
nil
|
260
|
+
else
|
261
|
+
true
|
262
|
+
end
|
245
263
|
@connection = connection
|
246
264
|
@has_max_page_size = max_page_size != :not_given
|
247
265
|
@max_page_size = max_page_size == :not_given ? nil : max_page_size
|
@@ -337,10 +355,12 @@ module GraphQL
|
|
337
355
|
def description(text = nil)
|
338
356
|
if text
|
339
357
|
@description = text
|
358
|
+
elsif defined?(@description)
|
359
|
+
@description
|
340
360
|
elsif @resolver_class
|
341
361
|
@description || @resolver_class.description
|
342
362
|
else
|
343
|
-
|
363
|
+
nil
|
344
364
|
end
|
345
365
|
end
|
346
366
|
|
@@ -516,19 +536,15 @@ module GraphQL
|
|
516
536
|
attr_writer :type
|
517
537
|
|
518
538
|
def type
|
519
|
-
if @resolver_class
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
# Not enough info to determine type
|
524
|
-
message = "Can't determine the return type for #{self.path}"
|
525
|
-
if @resolver_class
|
526
|
-
message += " (it has `resolver: #{@resolver_class}`, perhaps that class is missing a `type ...` declaration, or perhaps its type causes a cyclical loading issue)"
|
527
|
-
end
|
528
|
-
raise MissingReturnTypeError, message
|
529
|
-
else
|
530
|
-
Member::BuildType.parse_type(@return_type_expr, null: @return_type_null)
|
539
|
+
if @resolver_class
|
540
|
+
return_type = @return_type_expr || @resolver_class.type_expr
|
541
|
+
if return_type.nil?
|
542
|
+
raise MissingReturnTypeError, "Can't determine the return type for #{self.path} (it has `resolver: #{@resolver_class}`, perhaps that class is missing a `type ...` declaration, or perhaps its type causes a cyclical loading issue)"
|
531
543
|
end
|
544
|
+
nullable = @return_type_null.nil? ? @resolver_class.null : @return_type_null
|
545
|
+
Member::BuildType.parse_type(return_type, null: nullable)
|
546
|
+
else
|
547
|
+
@type ||= Member::BuildType.parse_type(@return_type_expr, null: @return_type_null)
|
532
548
|
end
|
533
549
|
rescue GraphQL::Schema::InvalidDocumentError, MissingReturnTypeError => err
|
534
550
|
# Let this propagate up
|
@@ -599,31 +615,94 @@ module GraphQL
|
|
599
615
|
# @param object [GraphQL::Schema::Object] An instance of some type class, wrapping an application object
|
600
616
|
# @param args [Hash] A symbol-keyed hash of Ruby keyword arguments. (Empty if no args)
|
601
617
|
# @param ctx [GraphQL::Query::Context]
|
602
|
-
def resolve(object, args,
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
618
|
+
def resolve(object, args, query_ctx)
|
619
|
+
# Unwrap the GraphQL object to get the application object.
|
620
|
+
application_object = object.object
|
621
|
+
method_receiver = nil
|
622
|
+
method_to_call = nil
|
623
|
+
method_args = nil
|
624
|
+
|
625
|
+
Schema::Validator.validate!(validators, application_object, query_ctx, args)
|
626
|
+
|
627
|
+
query_ctx.schema.after_lazy(self.authorized?(application_object, args, query_ctx)) do |is_authorized|
|
628
|
+
if is_authorized
|
629
|
+
with_extensions(object, args, query_ctx) do |obj, ruby_kwargs|
|
630
|
+
method_args = ruby_kwargs
|
631
|
+
if @resolver_class
|
632
|
+
if obj.is_a?(GraphQL::Schema::Object)
|
633
|
+
obj = obj.object
|
634
|
+
end
|
635
|
+
obj = @resolver_class.new(object: obj, context: query_ctx, field: self)
|
636
|
+
end
|
609
637
|
|
610
|
-
|
638
|
+
# Find a way to resolve this field, checking:
|
639
|
+
#
|
640
|
+
# - A method on the type instance;
|
641
|
+
# - Hash keys, if the wrapped object is a hash;
|
642
|
+
# - A method on the wrapped object;
|
643
|
+
# - Or, raise not implemented.
|
644
|
+
#
|
645
|
+
if obj.respond_to?(resolver_method)
|
646
|
+
method_to_call = resolver_method
|
647
|
+
method_receiver = obj
|
648
|
+
# Call the method with kwargs, if there are any
|
649
|
+
if ruby_kwargs.any?
|
650
|
+
obj.public_send(resolver_method, **ruby_kwargs)
|
651
|
+
else
|
652
|
+
obj.public_send(resolver_method)
|
653
|
+
end
|
654
|
+
elsif obj.object.is_a?(Hash)
|
655
|
+
inner_object = obj.object
|
656
|
+
if @dig_keys
|
657
|
+
inner_object.dig(*@dig_keys)
|
658
|
+
elsif inner_object.key?(@method_sym)
|
659
|
+
inner_object[@method_sym]
|
660
|
+
else
|
661
|
+
inner_object[@method_str]
|
662
|
+
end
|
663
|
+
elsif obj.object.respond_to?(@method_sym)
|
664
|
+
method_to_call = @method_sym
|
665
|
+
method_receiver = obj.object
|
666
|
+
if ruby_kwargs.any?
|
667
|
+
obj.object.public_send(@method_sym, **ruby_kwargs)
|
668
|
+
else
|
669
|
+
obj.object.public_send(@method_sym)
|
670
|
+
end
|
671
|
+
else
|
672
|
+
raise <<-ERR
|
673
|
+
Failed to implement #{@owner.graphql_name}.#{@name}, tried:
|
611
674
|
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
675
|
+
- `#{obj.class}##{resolver_method}`, which did not exist
|
676
|
+
- `#{obj.object.class}##{@method_sym}`, which did not exist
|
677
|
+
- Looking up hash key `#{@method_sym.inspect}` or `#{@method_str.inspect}` on `#{obj.object}`, but it wasn't a Hash
|
678
|
+
|
679
|
+
To implement this field, define one of the methods above (and check for typos)
|
680
|
+
ERR
|
681
|
+
end
|
617
682
|
end
|
683
|
+
else
|
684
|
+
raise GraphQL::UnauthorizedFieldError.new(object: application_object, type: object.class, context: query_ctx, field: self)
|
618
685
|
end
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
686
|
+
end
|
687
|
+
rescue GraphQL::UnauthorizedFieldError => err
|
688
|
+
err.field ||= self
|
689
|
+
begin
|
690
|
+
query_ctx.schema.unauthorized_field(err)
|
691
|
+
rescue GraphQL::ExecutionError => err
|
692
|
+
err
|
693
|
+
end
|
694
|
+
rescue GraphQL::UnauthorizedError => err
|
695
|
+
begin
|
696
|
+
query_ctx.schema.unauthorized_object(err)
|
697
|
+
rescue GraphQL::ExecutionError => err
|
698
|
+
err
|
699
|
+
end
|
700
|
+
rescue ArgumentError
|
701
|
+
if method_receiver && method_to_call
|
702
|
+
assert_satisfactory_implementation(method_receiver, method_to_call, method_args)
|
703
|
+
end
|
704
|
+
# if the line above doesn't raise, re-raise
|
705
|
+
raise
|
627
706
|
end
|
628
707
|
|
629
708
|
# @param ctx [GraphQL::Query::Context]
|
@@ -639,70 +718,6 @@ module GraphQL
|
|
639
718
|
|
640
719
|
private
|
641
720
|
|
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
721
|
def assert_satisfactory_implementation(receiver, method_name, ruby_kwargs)
|
707
722
|
method_defn = receiver.method(method_name)
|
708
723
|
unsatisfied_ruby_kwargs = ruby_kwargs.dup
|
@@ -148,7 +148,19 @@ module GraphQL
|
|
148
148
|
end
|
149
149
|
elsif defined?(@resolver_class) && @resolver_class
|
150
150
|
all_defns = {}
|
151
|
-
|
151
|
+
@resolver_class.all_field_argument_definitions.each do |arg_defn|
|
152
|
+
key = arg_defn.graphql_name
|
153
|
+
case (current_value = all_defns[key])
|
154
|
+
when nil
|
155
|
+
all_defns[key] = arg_defn
|
156
|
+
when Array
|
157
|
+
current_value << arg_defn
|
158
|
+
when GraphQL::Schema::Argument
|
159
|
+
all_defns[key] = [current_value, arg_defn]
|
160
|
+
else
|
161
|
+
raise "Invariant: Unexpected argument definition, #{current_value.class}: #{current_value.inspect}"
|
162
|
+
end
|
163
|
+
end
|
152
164
|
all_defns.merge!(own_arguments)
|
153
165
|
else
|
154
166
|
all_defns = own_arguments
|
@@ -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
|
|
@@ -92,6 +92,10 @@ module GraphQL
|
|
92
92
|
dummy.own_arguments
|
93
93
|
end
|
94
94
|
|
95
|
+
def all_field_argument_definitions
|
96
|
+
dummy.all_argument_definitions
|
97
|
+
end
|
98
|
+
|
95
99
|
# Also apply this argument to the input type:
|
96
100
|
def argument(*args, own_argument: false, **kwargs, &block)
|
97
101
|
it = input_type # make sure any inherited arguments are already added to it
|
@@ -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.5
|
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-28 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.2.32
|
600
|
+
signing_key:
|
601
601
|
specification_version: 4
|
602
602
|
summary: A GraphQL language and runtime for Ruby
|
603
603
|
test_files: []
|