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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 445a61081bd654dbd59e1d6472e027958f8f88bcb5b3f32351921c5f24f7c572
4
- data.tar.gz: d33ff0a628621fe4ccb4e19fbf727ce1e2cffe950697b33bc3682420bb6d2a81
3
+ metadata.gz: 248d39942011f4175ee77643a6e8d103d17d60ac73ee95418eb25e0eee2bbb27
4
+ data.tar.gz: 062c4d1402adf7f3612d888c6e00df91623340239996b6bc14db59ebbec41814
5
5
  SHA512:
6
- metadata.gz: 5d2f6f4c7556db47fec205db4007958f397e8a6b25c40a1838cf0789bf5e71162b0fe62305cb703ec5d5e61371513e978e9544a2f8622787c488d1fbad547af2
7
- data.tar.gz: c477b6a6c782e0bd5ef1eec47fc5dc6fa4ff56d21352c4ee1f6783debd8506c52cf6eace82c639166033b02436d4eebf5a18116624ad7ffd62d19ed8c5735dc0
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 rescue_from(error_class, error_handler)
13
+ def self.register_rescue_from(error_class, error_handlers, error_handler)
50
14
  subclasses_handlers = {}
51
15
  this_level_subclasses = []
52
16
  # During this traversal, do two things:
@@ -54,13 +18,12 @@ module GraphQL
54
18
  # and gather them up to be inserted _under_ this class
55
19
  # - Find the point in the index where this handler should be inserted
56
20
  # (That is, _under_ any superclasses, or at top-level, if there are no superclasses registered)
57
- handlers = @handlers[:subclass_handlers]
58
- while (handlers) do
21
+ while (error_handlers) do
59
22
  this_level_subclasses.clear
60
23
  # First, identify already-loaded handlers that belong
61
24
  # _under_ this one. (That is, they're handlers
62
25
  # for subclasses of `error_class`.)
63
- handlers.each do |err_class, handler|
26
+ error_handlers.each do |err_class, handler|
64
27
  if err_class < error_class
65
28
  subclasses_handlers[err_class] = handler
66
29
  this_level_subclasses << err_class
@@ -68,13 +31,13 @@ module GraphQL
68
31
  end
69
32
  # Any handlers that we'll be moving, delete them from this point in the index
70
33
  this_level_subclasses.each do |err_class|
71
- handlers.delete(err_class)
34
+ error_handlers.delete(err_class)
72
35
  end
73
36
 
74
37
  # See if any keys in this hash are superclasses of this new class:
75
- next_index_point = handlers.find { |err_class, handler| error_class < err_class }
38
+ next_index_point = error_handlers.find { |err_class, handler| error_class < err_class }
76
39
  if next_index_point
77
- handlers = next_index_point[1][:subclass_handlers]
40
+ error_handlers = next_index_point[1][:subclass_handlers]
78
41
  else
79
42
  # this new handler doesn't belong to any sub-handlers,
80
43
  # so insert it in the current set of `handlers`
@@ -83,40 +46,15 @@ module GraphQL
83
46
  end
84
47
  # Having found the point at which to insert this handler,
85
48
  # register it and merge any subclass handlers back in at this point.
86
- this_class_handlers = handlers[error_class]
49
+ this_class_handlers = error_handlers[error_class]
87
50
  this_class_handlers[:handler] = error_handler
88
51
  this_class_handlers[:subclass_handlers].merge!(subclasses_handlers)
89
52
  nil
90
53
  end
91
54
 
92
- # Call the given block with the schema's configured error handlers.
93
- #
94
- # If the block returns a lazy value, it's not wrapped with error handling. That area will have to be wrapped itself.
95
- #
96
- # @param ctx [GraphQL::Query::Context]
97
- # @return [Object] Either the result of the given block, or some object to replace the result, in case of error handling.
98
- def with_error_handling(ctx)
99
- yield
100
- rescue StandardError => err
101
- handler = find_handler_for(err.class)
102
- if handler
103
- runtime_info = ctx.namespace(:interpreter) || {}
104
- obj = runtime_info[:current_object]
105
- args = runtime_info[:current_arguments]
106
- args = args && args.keyword_arguments
107
- field = runtime_info[:current_field]
108
- if obj.is_a?(GraphQL::Schema::Object)
109
- obj = obj.object
110
- end
111
- handler[:handler].call(err, obj, args, ctx, field)
112
- else
113
- raise err
114
- end
115
- end
116
-
117
55
  # @return [Proc, nil] The handler for `error_class`, if one was registered on this schema or inherited
118
- def find_handler_for(error_class)
119
- handlers = @handlers[:subclass_handlers]
56
+ def self.find_handler_for(schema, error_class)
57
+ handlers = schema.error_handlers[:subclass_handlers]
120
58
  handler = nil
121
59
  while (handlers) do
122
60
  _err_class, next_handler = handlers.find { |err_class, handler| error_class <= err_class }
@@ -131,8 +69,8 @@ module GraphQL
131
69
  end
132
70
 
133
71
  # check for a handler from a parent class:
134
- if @schema.superclass.respond_to?(:error_handler) && (parent_errors = @schema.superclass.error_handler)
135
- parent_handler = parent_errors.find_handler_for(error_class)
72
+ if schema.superclass.respond_to?(:error_handlers)
73
+ parent_handler = find_handler_for(schema.superclass, error_class)
136
74
  end
137
75
 
138
76
  # If the inherited handler is more specific than the one defined here,
@@ -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.with_error_handling do
502
- 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
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
- query.with_error_handling do
875
- begin
876
- if trace
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
@@ -12,9 +12,6 @@ module GraphQL
12
12
  end
13
13
 
14
14
  class NullQuery
15
- def with_error_handling
16
- yield
17
- end
18
15
  end
19
16
 
20
17
  class NullSchema < GraphQL::Schema
data/lib/graphql/query.rb CHANGED
@@ -331,10 +331,8 @@ module GraphQL
331
331
  end
332
332
 
333
333
  # @api private
334
- def with_error_handling
335
- schema.error_handler.with_error_handling(context) do
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, context: nil, edge_class: nil)
38
- if context && context.schema.new_connections?
39
- conn_class = context.schema.connections.wrapper_for(collection)
40
- # The rest will be added by ConnectionExtension
41
- @connection = conn_class.new(collection, parent: parent, context: context, edge_class: edge_class)
42
- # Check if this connection supports it, to support old versions of GraphQL-Pro
43
- @edge = if @connection.respond_to?(:range_add_edge)
44
- @connection.range_add_edge(item)
45
- else
46
- @connection.edge_class.new(item, @connection)
47
- end
37
+ def initialize(collection:, item:, context:, parent: nil, edge_class: nil)
38
+ conn_class = context.schema.connections.wrapper_for(collection)
39
+ # The rest will be added by ConnectionExtension
40
+ @connection = conn_class.new(collection, parent: parent, context: context, edge_class: edge_class)
41
+ # Check if this connection supports it, to support old versions of GraphQL-Pro
42
+ @edge = if @connection.respond_to?(:range_add_edge)
43
+ @connection.range_add_edge(item)
48
44
  else
49
- connection_class = BaseConnection.connection_for_nodes(collection)
50
- @connection = connection_class.new(collection, {}, parent: parent, context: context)
51
- edge_class ||= Relay::Edge
52
- @edge = edge_class.new(item, @connection)
45
+ @connection.edge_class.new(item, @connection)
53
46
  end
54
47
 
55
48
  @parent = parent
@@ -107,6 +107,11 @@ module GraphQL
107
107
  if !pt.include?(owner) && owner.is_a?(Class)
108
108
  pt << owner
109
109
  end
110
+ int.interfaces.each do |indirect_int|
111
+ if indirect_int.is_a?(LateBoundType) && (indirect_int_type = get_type(indirect_int.graphql_name))
112
+ update_type_owner(owner, indirect_int_type)
113
+ end
114
+ end
110
115
  end
111
116
  end
112
117
  when nil
@@ -250,24 +250,30 @@ module GraphQL
250
250
  end
251
251
 
252
252
  loaded_value = nil
253
- coerced_value = context.schema.error_handler.with_error_handling(context) do
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 = context.query.with_error_handling do
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 = context.schema.error_handler.with_error_handling(context) do
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
@@ -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
- @return_type_expr && (@return_type_expr.is_a?(Array) || (@return_type_expr.is_a?(String) && @return_type_expr.include?("[")) || connection?)
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: true, description: nil, 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)
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
- @description = description
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
- @description
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 && (t = @resolver_class.type)
520
- t
521
- else
522
- @type ||= if @return_type_expr.nil?
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, ctx)
603
- if @resolve_proc
604
- raise "Can't run resolve proc for #{path} when using GraphQL::Execution::Interpreter"
605
- end
606
- begin
607
- # Unwrap the GraphQL object to get the application object.
608
- application_object = object.object
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
- Schema::Validator.validate!(validators, application_object, ctx, args)
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
- ctx.schema.after_lazy(self.authorized?(application_object, args, ctx)) do |is_authorized|
613
- if is_authorized
614
- public_send_field(object, args, ctx)
615
- else
616
- raise GraphQL::UnauthorizedFieldError.new(object: application_object, type: object.class, context: ctx, field: self)
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
- rescue GraphQL::UnauthorizedFieldError => err
620
- err.field ||= self
621
- ctx.schema.unauthorized_field(err)
622
- rescue GraphQL::UnauthorizedError => err
623
- ctx.schema.unauthorized_object(err)
624
- end
625
- rescue GraphQL::ExecutionError => err
626
- err
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
- all_defns.merge!(@resolver_class.own_field_arguments)
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
@@ -11,7 +11,7 @@ module GraphQL
11
11
  # @return [void]
12
12
  def directive(dir_class, **options)
13
13
  @own_directives ||= []
14
- remove_directive(dir_class)
14
+ remove_directive(dir_class) unless dir_class.repeatable?
15
15
  @own_directives << dir_class.new(self, **options)
16
16
  nil
17
17
  end
@@ -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
- @edge_type_class || find_inherited_value(:edge_type_class, Types::Relay::BaseEdge)
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
- @connection_type_class || find_inherited_value(:connection_type_class, Types::Relay::BaseConnection)
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
- context.query.with_error_handling do
55
- begin
56
- authorized?(object, context)
57
- rescue GraphQL::UnauthorizedError => err
58
- context.schema.unauthorized_object(err)
59
- end
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
@@ -216,8 +216,8 @@ module GraphQL
216
216
  get_argument(name, context)
217
217
  end
218
218
 
219
- def own_field_arguments
220
- own_arguments
219
+ def all_field_argument_definitions
220
+ all_argument_definitions
221
221
  end
222
222
 
223
223
  # Default `:resolve` set below.
@@ -42,11 +42,11 @@ module GraphQL
42
42
 
43
43
  def validate_non_null_input(value, ctx)
44
44
  coerced_result = begin
45
- ctx.query.with_error_handling do
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?
@@ -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
- error_handler.rescue_from(err_class, handler_block)
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
@@ -48,7 +48,9 @@ module GraphQL
48
48
  end
49
49
 
50
50
  def tracer
51
- options.fetch(:tracer, Datadog.tracer)
51
+ default_tracer = defined?(Datadog::Tracing) ? Datadog::Tracing : Datadog.tracer
52
+
53
+ options.fetch(:tracer, default_tracer)
52
54
  end
53
55
 
54
56
  def analytics_available?
@@ -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
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- VERSION = "2.0.2"
3
+ VERSION = "2.0.5"
4
4
  end
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.2
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-01 00:00:00.000000000 Z
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.1.6
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: []