Skip to content
This repository has been archived by the owner on Nov 8, 2017. It is now read-only.

Commit

Permalink
fix integer coercion.
Browse files Browse the repository at this point in the history
  • Loading branch information
bbuck committed Nov 9, 2015
1 parent 5f93f06 commit ba60c6c
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 24 deletions.
48 changes: 36 additions & 12 deletions scalars.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,44 +2,68 @@ package graphql

import (
"fmt"
"math"
"strconv"

"github.com/graphql-go/graphql/language/ast"
)

var (
MaxInt = 9007199254740991
MinInt = -9007199254740991
const (
MaxInt int64 = 9007199254740991
MinInt int64 = -9007199254740991
)

func coerceInt(value interface{}) interface{} {
switch value := value.(type) {
case bool:
if value == true {
return int(1)
return int64(1)
}
return int(0)
return int64(0)
case int:
return value
return int64(value)
case int8:
return int64(value)
case int16:
return int64(value)
case int32:
return int64(value)
case int64:
return intOrNil(value)
case uint:
return int64(value)
case uint8:
return int64(value)
case uint16:
return int64(value)
case uint32:
return int64(value)
case uint64:
if value > uint64(math.MaxInt64) {
// bypass intOrNil
return nil
}
return intOrNil(int64(value))
case float32:
return intOrNil(int(value))
return intOrNil(int64(value))
case float64:
return intOrNil(int(value))
return intOrNil(int64(value))
case string:
val, err := strconv.ParseFloat(value, 0)
if err != nil {
return nil
}
return coerceInt(val)
}
return int(0)

return nil
}

// Integers are only safe when between -(2^53 - 1) and 2^53 - 1 due to being
// encoded in JavaScript and represented in JSON as double-precision floating
// point numbers, as specified by IEEE 754.
func intOrNil(value int) interface{} {
if value <= MaxInt && value >= MinInt {
func intOrNil(value int64) interface{} {
if MinInt <= value && value <= MaxInt {
return value
}
return nil
Expand All @@ -52,7 +76,7 @@ var Int *Scalar = NewScalar(ScalarConfig{
ParseLiteral: func(valueAST ast.Value) interface{} {
switch valueAST := valueAST.(type) {
case *ast.IntValue:
if intValue, err := strconv.Atoi(valueAST.Value); err == nil {
if intValue, err := strconv.ParseInt(valueAST.Value, 10, 64); err == nil {
return intValue
}
}
Expand Down
25 changes: 13 additions & 12 deletions scalars_serialization_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,21 +28,22 @@ type boolSerializationTest struct {

func TestTypeSystem_Scalar_SerializesOutputInt(t *testing.T) {
tests := []intSerializationTest{
{1, 1},
{0, 0},
{-1, -1},
{float32(0.1), 0},
{float32(1.1), 1},
{float32(-1.1), -1},
{float32(1e5), 100000}, // Bigger than 2^32, but still representable as an Int
{9876504321, 9876504321},
{-9876504321, -9876504321},
{1, int64(1)},
{0, int64(0)},
{-1, int64(-1)},
{float32(0.1), int64(0)},
{float32(1.1), int64(1)},
{float32(-1.1), int64(-1)},
// Bigger than 2^32, but still representable as an Int
{float32(1e5), int64(100000)},
{9876504321, int64(9876504321)},
{-9876504321, int64(-9876504321)},
{float64(1e100), nil},
{float64(-1e100), nil},
{"-1.1", -1},
{"-1.1", int64(-1)},
{"one", nil},
{false, 0},
{true, 1},
{false, int64(0)},
{true, int64(1)},
}

for _, test := range tests {
Expand Down

0 comments on commit ba60c6c

Please sign in to comment.