22
33module GraphQL
44 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- #
165 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-
436 # Register this handler, updating the
447 # internal handler index to maintain least-to-most specific.
458 #
469 # @param error_class [Class<Exception>]
10+ # @param error_handlers [Hash]
4711 # @param error_handler [Proc]
4812 # @return [void]
49- def rescue_from ( error_class , error_handler )
13+ def self . register_rescue_from ( error_class , error_handlers , error_handler )
5014 subclasses_handlers = { }
5115 this_level_subclasses = [ ]
5216 # During this traversal, do two things:
5317 # - Identify any already-registered subclasses of this error class
5418 # and gather them up to be inserted _under_ this class
5519 # - Find the point in the index where this handler should be inserted
5620 # (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
5922 this_level_subclasses . clear
6023 # First, identify already-loaded handlers that belong
6124 # _under_ this one. (That is, they're handlers
6225 # for subclasses of `error_class`.)
63- handlers . each do |err_class , handler |
26+ error_handlers . each do |err_class , handler |
6427 if err_class < error_class
6528 subclasses_handlers [ err_class ] = handler
6629 this_level_subclasses << err_class
6730 end
6831 end
6932 # Any handlers that we'll be moving, delete them from this point in the index
7033 this_level_subclasses . each do |err_class |
71- handlers . delete ( err_class )
34+ error_handlers . delete ( err_class )
7235 end
7336
7437 # 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 }
7639 if next_index_point
77- handlers = next_index_point [ 1 ] [ :subclass_handlers ]
40+ error_handlers = next_index_point [ 1 ] [ :subclass_handlers ]
7841 else
7942 # this new handler doesn't belong to any sub-handlers,
8043 # so insert it in the current set of `handlers`
@@ -83,40 +46,15 @@ def rescue_from(error_class, error_handler)
8346 end
8447 # Having found the point at which to insert this handler,
8548 # 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 ]
8750 this_class_handlers [ :handler ] = error_handler
8851 this_class_handlers [ :subclass_handlers ] . merge! ( subclasses_handlers )
8952 nil
9053 end
9154
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-
11755 # @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 ]
12058 handler = nil
12159 while ( handlers ) do
12260 _err_class , next_handler = handlers . find { |err_class , handler | error_class <= err_class }
@@ -131,8 +69,8 @@ def find_handler_for(error_class)
13169 end
13270
13371 # 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 )
13674 end
13775
13876 # If the inherited handler is more specific than the one defined here,
0 commit comments