Skip to content

Commit

Permalink
Merge pull request #4052 from rmosolgo/list-each-error-handling
Browse files Browse the repository at this point in the history
Rescue errors raised by calling .each on list values
  • Loading branch information
rmosolgo committed May 24, 2022
2 parents 3e43ff4 + a4d9047 commit 0dde0e6
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 3 deletions.
26 changes: 23 additions & 3 deletions lib/graphql/execution/interpreter/runtime.rb
Expand Up @@ -754,11 +754,16 @@ def continue_field(path, value, owner_type, field, current_type, ast_node, next_
response_list = GraphQLResultArray.new(result_name, selection_result)
response_list.graphql_non_null_list_items = inner_type.non_null?
set_result(selection_result, result_name, response_list)

result_was_set = false
idx = 0
begin
list_value = begin
value.each do |inner_value|
break if dead_result?(response_list)
if !result_was_set
# Don't set the result unless `.each` is successful
set_result(selection_result, result_name, response_list)
result_was_set = true
end
next_path = path.dup
next_path << idx
this_idx = idx
Expand All @@ -772,6 +777,13 @@ def continue_field(path, value, owner_type, field, current_type, ast_node, next_
resolve_list_item(inner_value, inner_type, next_path, ast_node, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type)
end
end
# Maybe the list was empty and the block was never called.
if !result_was_set
set_result(selection_result, result_name, response_list)
result_was_set = true
end

response_list
rescue NoMethodError => err
# Ruby 2.2 doesn't have NoMethodError#receiver, can't check that one in this case. (It's been EOL since 2017.)
if err.name == :each && (err.respond_to?(:receiver) ? err.receiver == value : true)
Expand All @@ -781,9 +793,17 @@ def continue_field(path, value, owner_type, field, current_type, ast_node, next_
# This was some other NoMethodError -- let it bubble to reveal the real error.
raise
end
rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => ex_err
ex_err
rescue StandardError => err
begin
query.handle_or_reraise(err)
rescue GraphQL::ExecutionError => ex_err
ex_err
end
end

response_list
continue_value(path, list_value, owner_type, field, inner_type.non_null?, ast_node, result_name, selection_result)
else
raise "Invariant: Unhandled type kind #{current_type.kind} (#{current_type})"
end
Expand Down
19 changes: 19 additions & 0 deletions spec/graphql/execution/errors_spec.rb
Expand Up @@ -51,6 +51,11 @@ class ErrorBGrandchildClass < ErrorBChildClass; end
err.value
end

class ErrorList < Array
def each
raise ErrorB
end
end

class Thing < GraphQL::Schema::Object
def self.authorized?(obj, ctx)
Expand Down Expand Up @@ -157,6 +162,12 @@ def thing
def non_nullable_array
[nil]
end

field :error_in_each, [Int]

def error_in_each
ErrorList.new
end
end

query(Query)
Expand Down Expand Up @@ -309,5 +320,13 @@ def non_nullable_array
assert_equal({ "data" => nil, "errors" => [expected_error] }, res)
end
end

describe "when .each on a list type raises an error" do
it "rescues it properly" do
res = ErrorsTestSchema.execute("{ __typename errorInEach }")
expected_error = { "message" => "boom!", "locations"=>[{"line"=>1, "column"=>14}], "path"=>["errorInEach"] }
assert_equal({ "data" => { "__typename" => "Query", "errorInEach" => nil }, "errors" => [expected_error] }, res)
end
end
end
end

0 comments on commit 0dde0e6

Please sign in to comment.