Skip to content

Commit a539cd1

Browse files
authored
Merge pull request #5257 from rmosolgo/fix-invalid-null-error-handling
Improve default InvalidNullError handling
2 parents 46f8b69 + b74c41b commit a539cd1

File tree

8 files changed

+35
-23
lines changed

8 files changed

+35
-23
lines changed

lib/graphql/execution/interpreter/runtime.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,7 @@ def continue_value(value, field, is_non_null, ast_node, result_name, selection_r
473473
# When this comes from a list item, use the parent object:
474474
parent_type = selection_result.is_a?(GraphQLResultArray) ? selection_result.graphql_parent.graphql_result_type : selection_result.graphql_result_type
475475
# This block is called if `result_name` is not dead. (Maybe a previous invalid nil caused it be marked dead.)
476-
err = parent_type::InvalidNullError.new(parent_type, field, value)
476+
err = parent_type::InvalidNullError.new(parent_type, field, value, ast_node)
477477
schema.type_error(err, context)
478478
end
479479
else

lib/graphql/invalid_null_error.rb

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
module GraphQL
33
# Raised automatically when a field's resolve function returns `nil`
44
# for a non-null field.
5-
class InvalidNullError < GraphQL::RuntimeTypeError
5+
class InvalidNullError < GraphQL::Error
66
# @return [GraphQL::BaseType] The owner of {#field}
77
attr_reader :parent_type
88

@@ -12,23 +12,17 @@ class InvalidNullError < GraphQL::RuntimeTypeError
1212
# @return [nil, GraphQL::ExecutionError] The invalid value for this field
1313
attr_reader :value
1414

15-
def initialize(parent_type, field, value)
15+
# @return [GraphQL::Language::Nodes::Field] the field where the error occurred
16+
attr_reader :ast_node
17+
18+
def initialize(parent_type, field, value, ast_node)
1619
@parent_type = parent_type
1720
@field = field
1821
@value = value
22+
@ast_node = ast_node
1923
super("Cannot return null for non-nullable field #{@parent_type.graphql_name}.#{@field.graphql_name}")
2024
end
2125

22-
# @return [Hash] An entry for the response's "errors" key
23-
def to_h
24-
{ "message" => message }
25-
end
26-
27-
# @deprecated always false
28-
def parent_error?
29-
false
30-
end
31-
3226
class << self
3327
attr_accessor :parent_class
3428

lib/graphql/schema.rb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1298,7 +1298,10 @@ def unauthorized_field(unauthorized_error)
12981298
def type_error(type_error, ctx)
12991299
case type_error
13001300
when GraphQL::InvalidNullError
1301-
ctx.errors << type_error
1301+
execution_error = GraphQL::ExecutionError.new(type_error.message, ast_node: type_error.ast_node)
1302+
execution_error.path = ctx[:current_path]
1303+
1304+
ctx.errors << execution_error
13021305
when GraphQL::UnresolvedTypeError, GraphQL::StringEncodingError, GraphQL::IntegerEncodingError
13031306
raise type_error
13041307
when GraphQL::IntegerDecodingError

spec/graphql/execution/errors_spec.rb

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -307,15 +307,19 @@ def non_nullable_array
307307
it "outputs the appropriate error message when using non-interpreter schema" do
308308
res = ErrorsTestSchemaWithoutInterpreter.execute("{ nonNullableArray }")
309309
expected_error = {
310-
"message" => "Cannot return null for non-nullable field Query.nonNullableArray"
310+
"message" => "Cannot return null for non-nullable field Query.nonNullableArray",
311+
"path" => ["nonNullableArray", 0],
312+
"locations" => [{ "line" => 1, "column" => 3 }]
311313
}
312314
assert_equal({ "data" => nil, "errors" => [expected_error] }, res)
313315
end
314316

315317
it "outputs the appropriate error message when using interpreter schema" do
316318
res = ErrorsTestSchema.execute("{ nonNullableArray }")
317319
expected_error = {
318-
"message" => "Cannot return null for non-nullable field Query.nonNullableArray"
320+
"message" => "Cannot return null for non-nullable field Query.nonNullableArray",
321+
"path" => ["nonNullableArray", 0],
322+
"locations" => [{ "line" => 1, "column" => 3 }]
319323
}
320324
assert_equal({ "data" => nil, "errors" => [expected_error] }, res)
321325
end

spec/graphql/execution/multiplex_spec.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,11 @@ def multiplex(*a, **kw)
114114
},
115115
{
116116
"data"=>{"invalidNestedNull"=>{"value" => 2,"nullableNestedSum" => nil}},
117-
"errors"=>[{"message"=>"Cannot return null for non-nullable field LazySum.nestedSum"}],
117+
"errors"=>[{
118+
"message"=>"Cannot return null for non-nullable field LazySum.nestedSum",
119+
"path"=>["invalidNestedNull", "nullableNestedSum", "nestedSum"],
120+
"locations"=>[{"line"=>5, "column"=>11}],
121+
}],
118122
},
119123
{
120124
"errors" => [{

spec/graphql/non_null_type_spec.rb

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@
77
query_string = %|{ cow { name cantBeNullButIs } }|
88
result = Dummy::Schema.execute(query_string)
99
assert_equal({"cow" => nil }, result["data"])
10-
assert_equal([{"message"=>"Cannot return null for non-nullable field Cow.cantBeNullButIs"}], result["errors"])
10+
assert_equal([{
11+
"message" => "Cannot return null for non-nullable field Cow.cantBeNullButIs",
12+
"path" => ["cow", "cantBeNullButIs"],
13+
"locations" => [{"line" => 1, "column" => 14}],
14+
}], result["errors"])
1115
end
1216

1317
it "propagates the null up to the next nullable field" do
@@ -26,7 +30,11 @@
2630
|
2731
result = Dummy::Schema.execute(query_string)
2832
assert_nil(result["data"])
29-
assert_equal([{"message"=>"Cannot return null for non-nullable field DeepNonNull.nonNullInt"}], result["errors"])
33+
assert_equal([{
34+
"message" => "Cannot return null for non-nullable field DeepNonNull.nonNullInt",
35+
"path" => ["nn1", "nn2", "nn3", "nni3"],
36+
"locations" => [{"line" => 8, "column" => 15}],
37+
}], result["errors"])
3038
end
3139

3240
describe "when type_error is configured to raise an error" do

spec/graphql/query/executor_spec.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,9 @@
147147
"data" => { "cow" => nil },
148148
"errors" => [
149149
{
150-
"message" => "Cannot return null for non-nullable field Cow.cantBeNullButIs"
150+
"message" => "Cannot return null for non-nullable field Cow.cantBeNullButIs",
151+
"path" => ["cow", "cantBeNullButIs"],
152+
"locations" => [{ "line" => 1, "column" => 28 }]
151153
}
152154
]
153155
}

spec/graphql/schema/mutation_spec.rb

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,9 +113,6 @@
113113
query_str = "mutation { returnInvalidNull { int } }"
114114
response = Jazz::Schema.execute(query_str)
115115
assert_equal ["Cannot return null for non-nullable field ReturnInvalidNullPayload.int"], response["errors"].map { |e| e["message"] }
116-
error = response.query.context.errors.first
117-
assert_instance_of Jazz::ReturnInvalidNull.payload_type::InvalidNullError, error
118-
assert_equal "Jazz::ReturnInvalidNull::ReturnInvalidNullPayload::InvalidNullError", error.class.inspect
119116
end
120117
end
121118

0 commit comments

Comments
 (0)