Skip to content

Update Int and Float coercion logic to be spec compliant #5306

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
4 changes: 1 addition & 3 deletions lib/graphql/schema.rb
Original file line number Diff line number Diff line change
@@ -1304,10 +1304,8 @@ def type_error(type_error, ctx)
execution_error.path = ctx[:current_path]

ctx.errors << execution_error
when GraphQL::UnresolvedTypeError, GraphQL::StringEncodingError, GraphQL::IntegerEncodingError
when GraphQL::UnresolvedTypeError, GraphQL::StringEncodingError
raise type_error
when GraphQL::IntegerDecodingError
nil
end
end

14 changes: 12 additions & 2 deletions lib/graphql/types/float.rb
Original file line number Diff line number Diff line change
@@ -6,11 +6,21 @@ class Float < GraphQL::Schema::Scalar
description "Represents signed double-precision fractional values as specified by [IEEE 754](https://en.wikipedia.org/wiki/IEEE_floating_point)."

def self.coerce_input(value, _ctx)
value.is_a?(Numeric) ? value.to_f : nil
if value.is_a?(Numeric)
value.to_f
else
raise GraphQL::CoercionError, "Float cannot represent non numeric value: #{value.inspect}"
end
end

def self.coerce_result(value, _ctx)
value.to_f
coerced_value = Float(value, exception: false)

if coerced_value.nil? || !coerced_value.finite?
raise GraphQL::CoercionError, "Float cannot represent non numeric value: #{value.inspect}"
end

coerced_value
end

default_scalar true
11 changes: 5 additions & 6 deletions lib/graphql/types/int.rb
Original file line number Diff line number Diff line change
@@ -15,18 +15,17 @@ def self.coerce_input(value, ctx)
if value >= MIN && value <= MAX
value
else
err = GraphQL::IntegerDecodingError.new(value)
ctx.schema.type_error(err, ctx)
raise GraphQL::CoercionError.new("Int cannot represent non 32-bit signed integer value: #{value.inspect}")
end
end

def self.coerce_result(value, ctx)
value = value.to_i
if value >= MIN && value <= MAX
value = Integer(value, exception: false)

if value && (value >= MIN && value <= MAX)
value
else
err = GraphQL::IntegerEncodingError.new(value, context: ctx)
ctx.schema.type_error(err, ctx)
raise GraphQL::CoercionError.new("Int cannot represent non 32-bit signed integer value: #{value.inspect}")
end
end

37 changes: 34 additions & 3 deletions spec/graphql/types/float_spec.rb
Original file line number Diff line number Diff line change
@@ -11,9 +11,40 @@
end

it "rejects other types" do
assert_nil GraphQL::Types::Float.coerce_isolated_input("55")
assert_nil GraphQL::Types::Float.coerce_isolated_input(true)
assert_nil GraphQL::Types::Float.coerce_isolated_input(enum)
assert_raises(GraphQL::CoercionError) do
GraphQL::Types::Float.coerce_isolated_input("55")
end

assert_raises(GraphQL::CoercionError) do
GraphQL::Types::Float.coerce_isolated_input(true)
end

assert_raises(GraphQL::CoercionError) do
GraphQL::Types::Float.coerce_isolated_input(enum)
end
end
end

describe "coerce_result" do
it "coercess ints and floats" do
err_ctx = GraphQL::Query.new(Dummy::Schema, "{ __typename }").context

assert_equal 1.0, GraphQL::Types::Float.coerce_result(1, err_ctx)
assert_equal 1.0, GraphQL::Types::Float.coerce_result("1", err_ctx)
assert_equal 1.0, GraphQL::Types::Float.coerce_result("1.0", err_ctx)
assert_equal 6.1, GraphQL::Types::Float.coerce_result(6.1, err_ctx)
end

it "rejects other types" do
err_ctx = GraphQL::Query.new(Dummy::Schema, "{ __typename }").context

assert_raises(GraphQL::CoercionError) do
GraphQL::Types::Float.coerce_result("foo", err_ctx)
end

assert_raises(GraphQL::CoercionError) do
GraphQL::Types::Float.coerce_result(1.0 / 0, err_ctx)
end
end
end
end
25 changes: 13 additions & 12 deletions spec/graphql/types/int_spec.rb
Original file line number Diff line number Diff line change
@@ -13,8 +13,14 @@
assert_nil GraphQL::Types::Int.coerce_isolated_input("55")
assert_nil GraphQL::Types::Int.coerce_isolated_input(true)
assert_nil GraphQL::Types::Int.coerce_isolated_input(6.1)
assert_nil GraphQL::Types::Int.coerce_isolated_input(2**31)
assert_nil GraphQL::Types::Int.coerce_isolated_input(-(2**31 + 1))

assert_raises(GraphQL::CoercionError) do
GraphQL::Types::Int.coerce_isolated_input(2**31)
end

assert_raises(GraphQL::CoercionError) do
GraphQL::Types::Int.coerce_isolated_input(-(2**31 + 1))
end
end

describe "handling boundaries" do
@@ -26,21 +32,16 @@
assert_equal(-(2**31), GraphQL::Types::Int.coerce_result(-(2**31), context))
end

it "replaces values, if configured to do so" do
assert_equal Dummy::Schema::MAGIC_INT_COERCE_VALUE, GraphQL::Types::Int.coerce_result(99**99, context)
end

it "raises on values out of bounds" do
err_ctx = GraphQL::Query.new(Dummy::Schema, "{ __typename }").context
assert_raises(GraphQL::IntegerEncodingError) { GraphQL::Types::Int.coerce_result(2**31, err_ctx) }
err = assert_raises(GraphQL::IntegerEncodingError) { GraphQL::Types::Int.coerce_result(-(2**31 + 1), err_ctx) }
assert_equal "Integer out of bounds: -2147483649. Consider using ID or GraphQL::Types::BigInt instead.", err.message
assert_raises(GraphQL::CoercionError) { GraphQL::Types::Int.coerce_result(2**31, err_ctx) }
err = assert_raises(GraphQL::CoercionError) { GraphQL::Types::Int.coerce_result(-(2**31 + 1), err_ctx) }
assert_equal "Int cannot represent non 32-bit signed integer value: -2147483649", err.message

err = assert_raises GraphQL::IntegerEncodingError do
err = assert_raises GraphQL::CoercionError do
Dummy::Schema.execute("{ hugeInteger }")
end
expected_err = "Integer out of bounds: 2147483648 @ hugeInteger (Query.hugeInteger). Consider using ID or GraphQL::Types::BigInt instead."
assert_equal expected_err, err.message
assert_equal "Int cannot represent non 32-bit signed integer value: 2147483648", err.message
end
end
end
11 changes: 0 additions & 11 deletions spec/support/dummy/schema.rb
Original file line number Diff line number Diff line change
@@ -557,17 +557,6 @@ def self.resolve_type(type, obj, ctx)
-> { Schema.types[obj.class.name.split("::").last] }
end

# This is used to confirm that the hook is called:
MAGIC_INT_COERCE_VALUE = -1

def self.type_error(err, ctx)
if err.is_a?(GraphQL::IntegerEncodingError) && err.integer_value == 99**99
MAGIC_INT_COERCE_VALUE
else
super
end
end

use GraphQL::Dataloader

lazy_resolve(Proc, :call)
Loading
Oops, something went wrong.