Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deprecate error_bubbling(true) #4813

Merged
merged 2 commits into from
Jan 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/graphql/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -743,6 +743,7 @@ def interpreter?

def error_bubbling(new_error_bubbling = nil)
if !new_error_bubbling.nil?
warn("error_bubbling(#{new_error_bubbling.inspect}) is deprecated; the default value of `false` will be the only option in GraphQL-Ruby 3.0")
@error_bubbling = new_error_bubbling
else
@error_bubbling.nil? ? find_inherited_value(:error_bubbling) : @error_bubbling
Expand Down
45 changes: 11 additions & 34 deletions spec/graphql/schema/warden_spec.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# frozen_string_literal: true
require "spec_helper"
include ErrorBubblingHelpers

module MaskHelpers
def self.build_mask(only:, except:)
Expand Down Expand Up @@ -774,39 +773,17 @@ def self.visible?(member, context)
assert_equal expected_errors, error_messages(res)
end

describe "with error bubbling disabled" do
it "isn't a valid literal input" do
without_error_bubbling(MaskHelpers::Schema) do
query_string = %|
{
languages(within: {latitude: 1.0, longitude: 2.2, miles: 3.3}) { name }
}|
res = MaskHelpers.query_with_mask(query_string, mask)
expected_errors =
[
"InputObject 'WithinInput' doesn't accept argument 'miles'"
]
assert_equal expected_errors, error_messages(res)
end
end
end

describe "with error bubbling enabled" do
it "isn't a valid literal input" do
with_error_bubbling(MaskHelpers::Schema) do
query_string = %|
{
languages(within: {latitude: 1.0, longitude: 2.2, miles: 3.3}) { name }
}|
res = MaskHelpers.query_with_mask(query_string, mask)
expected_errors =
[
"InputObject 'WithinInput' doesn't accept argument 'miles'",
"Argument 'within' on Field 'languages' has an invalid value ({latitude: 1.0, longitude: 2.2, miles: 3.3}). Expected type 'WithinInput'.",
]
assert_equal expected_errors, error_messages(res)
end
end
it "isn't a valid literal input" do
query_string = %|
{
languages(within: {latitude: 1.0, longitude: 2.2, miles: 3.3}) { name }
}|
res = MaskHelpers.query_with_mask(query_string, mask)
expected_errors =
[
"InputObject 'WithinInput' doesn't accept argument 'miles'"
]
assert_equal expected_errors, error_messages(res)
end

it "isn't a valid variable input" do
Expand Down
4 changes: 0 additions & 4 deletions spec/graphql/schema_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ class CustomSubscriptions < GraphQL::Subscriptions::ActionCableSubscriptions
max_depth 2
default_max_page_size 3
default_page_size 2
error_bubbling false
disable_introspection_entry_points
orphan_types Jazz::Ensemble
introspection Module.new
Expand Down Expand Up @@ -73,7 +72,6 @@ class CustomSubscriptions < GraphQL::Subscriptions::ActionCableSubscriptions
assert_equal base_schema.max_depth, schema.max_depth
assert_equal base_schema.default_max_page_size, schema.default_max_page_size
assert_equal base_schema.default_page_size, schema.default_page_size
assert_equal base_schema.error_bubbling, schema.error_bubbling
assert_equal base_schema.orphan_types, schema.orphan_types
assert_equal base_schema.context_class, schema.context_class
assert_equal base_schema.directives, schema.directives
Expand Down Expand Up @@ -133,7 +131,6 @@ class CustomSubscriptions < GraphQL::Subscriptions::ActionCableSubscriptions
schema.max_depth(20)
schema.default_max_page_size(30)
schema.default_page_size(15)
schema.error_bubbling(true)
schema.orphan_types(Jazz::InstrumentType)
schema.directives([DummyFeature2])
query_analyzer = Object.new
Expand All @@ -155,7 +152,6 @@ class CustomSubscriptions < GraphQL::Subscriptions::ActionCableSubscriptions
assert_equal 20, schema.max_depth
assert_equal 30, schema.default_max_page_size
assert_equal 15, schema.default_page_size
assert schema.error_bubbling
assert_equal [Jazz::Ensemble, Jazz::InstrumentType], schema.orphan_types
assert_equal schema.directives, GraphQL::Schema.default_directives.merge(DummyFeature1.graphql_name => DummyFeature1, DummyFeature2.graphql_name => DummyFeature2)
assert_equal base_schema.query_analyzers + [query_analyzer], schema.query_analyzers
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

describe GraphQL::StaticValidation::ArgumentLiteralsAreCompatible do
include StaticValidationHelpers
include ErrorBubblingHelpers

let(:query_string) {%|
query getCheese {
Expand All @@ -21,99 +20,42 @@
}
|}

describe "with error bubbling disabled" do
it "finds undefined or missing-required arguments to fields and directives" do
without_error_bubbling(schema) do
# `wacky` above is handled by ArgumentsAreDefined, missingSource is handled by RequiredInputObjectAttributesArePresent
# so only 4 are tested below
assert_equal(6, errors.length)

query_root_error = {
"message"=>"Argument 'id' on Field 'stringCheese' has an invalid value (\"aasdlkfj\"). Expected type 'Int!'.",
"locations"=>[{"line"=>3, "column"=>7}],
"path"=>["query getCheese", "stringCheese", "id"],
"extensions"=>{"code"=>"argumentLiteralsIncompatible", "typeName"=>"Field", "argumentName"=>"id"},
}
assert_includes(errors, query_root_error)

directive_error = {
"message"=>"Argument 'if' on Directive 'skip' has an invalid value (\"whatever\"). Expected type 'Boolean!'.",
"locations"=>[{"line"=>4, "column"=>30}],
"path"=>["query getCheese", "cheese", "source", "if"],
"extensions"=>{"code"=>"argumentLiteralsIncompatible", "typeName"=>"Directive", "argumentName"=>"if"},
}
assert_includes(errors, directive_error)

input_object_field_error = {
"message"=>"Argument 'source' on InputObject 'DairyProductInput' has an invalid value (1.1). Expected type 'DairyAnimal!'.",
"locations"=>[{"line"=>6, "column"=>39}],
"path"=>["query getCheese", "badSource", "product", 0, "source"],
"extensions"=>{"code"=>"argumentLiteralsIncompatible", "typeName"=>"InputObject", "argumentName"=>"source"},
}
assert_includes(errors, input_object_field_error)

fragment_error = {
"message"=>"Argument 'source' on Field 'similarCheese' has an invalid value (4.5). Expected type '[DairyAnimal!]!'.",
"locations"=>[{"line"=>13, "column"=>7}],
"path"=>["fragment cheeseFields", "similarCheese", "source"],
"extensions"=> {"code"=>"argumentLiteralsIncompatible", "typeName"=>"Field", "argumentName"=>"source"}
}
assert_includes(errors, fragment_error)
end
end
it 'works with error bubbling enabled' do
with_error_bubbling(schema) do
assert_equal(9, errors.length)

query_root_error = {
"message"=>"Argument 'id' on Field 'stringCheese' has an invalid value (\"aasdlkfj\"). Expected type 'Int!'.",
"locations"=>[{"line"=>3, "column"=>7}],
"path"=>["query getCheese", "stringCheese", "id"],
"extensions"=>{"code"=>"argumentLiteralsIncompatible", "typeName"=>"Field", "argumentName"=>"id"},
}
assert_includes(errors, query_root_error)

directive_error = {
"message"=>"Argument 'if' on Directive 'skip' has an invalid value (\"whatever\"). Expected type 'Boolean!'.",
"locations"=>[{"line"=>4, "column"=>30}],
"path"=>["query getCheese", "cheese", "source", "if"],
"extensions"=>{"code"=>"argumentLiteralsIncompatible", "typeName"=>"Directive", "argumentName"=>"if"},
}
assert_includes(errors, directive_error)
it "finds undefined or missing-required arguments to fields and directives" do
# `wacky` above is handled by ArgumentsAreDefined, missingSource is handled by RequiredInputObjectAttributesArePresent
# so only 4 are tested below
assert_equal(6, errors.length)

input_object_error = {
"message"=>"Argument 'product' on Field 'badSource' has an invalid value ({source: 1.1}). Expected type '[DairyProductInput]'.",
"locations"=>[{"line"=>6, "column"=>7}],
"path"=>["query getCheese", "badSource", "product"],
"extensions"=>{"code"=>"argumentLiteralsIncompatible", "typeName"=>"Field", "argumentName"=>"product"},
}
assert_includes(errors, input_object_error)
query_root_error = {
"message"=>"Argument 'id' on Field 'stringCheese' has an invalid value (\"aasdlkfj\"). Expected type 'Int!'.",
"locations"=>[{"line"=>3, "column"=>7}],
"path"=>["query getCheese", "stringCheese", "id"],
"extensions"=>{"code"=>"argumentLiteralsIncompatible", "typeName"=>"Field", "argumentName"=>"id"},
}
assert_includes(errors, query_root_error)

input_object_field_error = {
"message"=>"Argument 'source' on InputObject 'DairyProductInput' has an invalid value (1.1). Expected type 'DairyAnimal!'.",
"locations"=>[{"line"=>6, "column"=>39}],
"path"=>["query getCheese", "badSource", "product", 0, "source"],
"extensions"=>{"code"=>"argumentLiteralsIncompatible", "typeName"=>"InputObject", "argumentName"=>"source"},
}
assert_includes(errors, input_object_field_error)
directive_error = {
"message"=>"Argument 'if' on Directive 'skip' has an invalid value (\"whatever\"). Expected type 'Boolean!'.",
"locations"=>[{"line"=>4, "column"=>30}],
"path"=>["query getCheese", "cheese", "source", "if"],
"extensions"=>{"code"=>"argumentLiteralsIncompatible", "typeName"=>"Directive", "argumentName"=>"if"},
}
assert_includes(errors, directive_error)

missing_required_field_error = {
"message"=>"Argument 'product' on Field 'missingSource' has an invalid value ([{fatContent: 1.1}]). Expected type '[DairyProductInput]'.",
"locations"=>[{"line"=>7, "column"=>7}],
"path"=>["query getCheese", "missingSource", "product"],
"extensions"=>{"code"=>"argumentLiteralsIncompatible", "typeName"=>"Field", "argumentName"=>"product"}
}
assert_includes(errors, missing_required_field_error)
input_object_field_error = {
"message"=>"Argument 'source' on InputObject 'DairyProductInput' has an invalid value (1.1). Expected type 'DairyAnimal!'.",
"locations"=>[{"line"=>6, "column"=>39}],
"path"=>["query getCheese", "badSource", "product", 0, "source"],
"extensions"=>{"code"=>"argumentLiteralsIncompatible", "typeName"=>"InputObject", "argumentName"=>"source"},
}
assert_includes(errors, input_object_field_error)

fragment_error = {
"message"=>"Argument 'source' on Field 'similarCheese' has an invalid value (4.5). Expected type '[DairyAnimal!]!'.",
"locations"=>[{"line"=>13, "column"=>7}],
"path"=>["fragment cheeseFields", "similarCheese", "source"],
"extensions"=> {"code"=>"argumentLiteralsIncompatible", "typeName"=>"Field", "argumentName"=>"source"}
}
assert_includes(errors, fragment_error)
end
end
fragment_error = {
"message"=>"Argument 'source' on Field 'similarCheese' has an invalid value (4.5). Expected type '[DairyAnimal!]!'.",
"locations"=>[{"line"=>13, "column"=>7}],
"path"=>["fragment cheeseFields", "similarCheese", "source"],
"extensions"=> {"code"=>"argumentLiteralsIncompatible", "typeName"=>"Field", "argumentName"=>"source"}
}
assert_includes(errors, fragment_error)
end

describe "using input objects for enums it adds an error" do
Expand All @@ -123,16 +65,8 @@
}
GRAPHQL
}
it "works with error bubbling disabled" do
without_error_bubbling(schema) do
assert_equal 1, errors.length
end
end

it "works with error bubbling enabled" do
with_error_bubbling(schema) do
assert_equal 2, errors.length
end
it "works" do
assert_equal 1, errors.length
end
end

Expand All @@ -155,18 +89,9 @@
}
end

it "works with error bubbling disabled" do
without_error_bubbling(schema) do
assert_includes(errors, enum_invalid_for_id_error)
assert_equal 1, errors.length
end
end

it "works with error bubbling enabled" do
with_error_bubbling(schema) do
assert_includes(errors, enum_invalid_for_id_error)
assert_equal 1, errors.length
end
it "works" do
assert_includes(errors, enum_invalid_for_id_error)
assert_equal 1, errors.length
end
end

Expand Down Expand Up @@ -276,42 +201,20 @@
}
|}

describe "it finds errors" do
it "works with error bubbling disabled" do
without_error_bubbling(schema) do
assert_equal 1, errors.length
refute_includes errors, {
"message"=>"Argument 'arg' on Field 'field' has an invalid value ({a: null, b: null}). Expected type 'Input'.",
"locations"=>[{"line"=>3, "column"=>11}],
"path"=>["query", "field", "arg"],
"extensions"=>{"code"=>"argumentLiteralsIncompatible", "typeName"=>"Field", "argumentName"=>"arg"}
}
assert_includes errors, {
"message"=>"Argument 'b' on InputObject 'Input' has an invalid value (null). Expected type 'Int!'.",
"locations"=>[{"line"=>3, "column"=>22}],
"path"=>["query", "field", "arg", "b"],
"extensions"=>{"code"=>"argumentLiteralsIncompatible", "typeName"=>"InputObject", "argumentName"=>"b"}
}
end
end
it "works with error bubbling enabled" do
with_error_bubbling(schema) do
assert_equal 2, errors.length
assert_includes errors, {
"message"=>"Argument 'arg' on Field 'field' has an invalid value ({a: null, b: null}). Expected type 'Input'.",
"locations"=>[{"line"=>3, "column"=>11}],
"path"=>["query", "field", "arg"],
"extensions"=>{"code"=>"argumentLiteralsIncompatible", "typeName"=>"Field", "argumentName"=>"arg"}
}

assert_includes errors, {
"message"=>"Argument 'b' on InputObject 'Input' has an invalid value (null). Expected type 'Int!'.",
"locations"=>[{"line"=>3, "column"=>22}],
"path"=>["query", "field", "arg", "b"],
"extensions"=>{"code"=>"argumentLiteralsIncompatible", "typeName"=>"InputObject", "argumentName"=>"b"}
}
end
end
it "it finds errors" do
assert_equal 1, errors.length
refute_includes errors, {
"message"=>"Argument 'arg' on Field 'field' has an invalid value ({a: null, b: null}). Expected type 'Input'.",
"locations"=>[{"line"=>3, "column"=>11}],
"path"=>["query", "field", "arg"],
"extensions"=>{"code"=>"argumentLiteralsIncompatible", "typeName"=>"Field", "argumentName"=>"arg"}
}
assert_includes errors, {
"message"=>"Argument 'b' on InputObject 'Input' has an invalid value (null). Expected type 'Int!'.",
"locations"=>[{"line"=>3, "column"=>22}],
"path"=>["query", "field", "arg", "b"],
"extensions"=>{"code"=>"argumentLiteralsIncompatible", "typeName"=>"InputObject", "argumentName"=>"b"}
}
end
end
end
Expand Down Expand Up @@ -472,22 +375,11 @@ def email(value:)
}

describe "sets deep error message from a CoercionError if raised" do
it "works with error bubbling enabled" do
with_error_bubbling(schema) do
assert_equal 3, errors.length
assert_includes(errors, from_error)
assert_includes(errors, to_error)
assert_includes(errors, bubbling_error)
end
end

it "works without error bubbling enabled" do
without_error_bubbling(schema) do
assert_equal 2, errors.length
assert_includes(errors, from_error)
assert_includes(errors, to_error)
refute_includes(errors, bubbling_error)
end
it "works" do
assert_equal 2, errors.length
assert_includes(errors, from_error)
assert_includes(errors, to_error)
refute_includes(errors, bubbling_error)
end
end
end
Expand Down
Loading