Skip to content

feat(GraphQL): GraphQL now has Int64 as scalar type #6200

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

Merged
merged 4 commits into from
Aug 18, 2020
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
4 changes: 3 additions & 1 deletion graphql/e2e/common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ type post struct {
Text string `json:"text,omitempty"`
Tags []string `json:"tags,omitempty"`
NumLikes int `json:"numLikes,omitempty"`
NumViews int64 `json:"numViews,omitempty"`
IsPublished bool `json:"isPublished,omitempty"`
PostType string `json:"postType,omitempty"`
Author *author `json:"author,omitempty"`
Expand Down Expand Up @@ -247,7 +248,8 @@ func RunAll(t *testing.T) {
t.Run("queries with error", queriesWithError)
t.Run("date filters", dateFilters)
t.Run("float filters", floatFilters)
t.Run("int filters", intFilters)
t.Run("Int filters", int32Filters)
t.Run("Int64 filters", int64Filters)
t.Run("boolean filters", booleanFilters)
t.Run("term filters", termFilters)
t.Run("full text filters", fullTextFilters)
Expand Down
8 changes: 6 additions & 2 deletions graphql/e2e/common/mutation.go
Original file line number Diff line number Diff line change
Expand Up @@ -714,6 +714,7 @@ func addPost(t *testing.T, authorID, countryID string,
isPublished
tags
numLikes
numViews
author {
id
name
Expand All @@ -730,6 +731,7 @@ func addPost(t *testing.T, authorID, countryID string,
"text": "This post is just a test.",
"isPublished": true,
"numLikes": 1000,
"numViews": 9007199254740991, // (2^53)-1
"tags": []string{"example", "test"},
"author": map[string]interface{}{"id": authorID},
}},
Expand All @@ -743,6 +745,7 @@ func addPost(t *testing.T, authorID, countryID string,
"isPublished": true,
"tags": ["example", "test"],
"numLikes": 1000,
"numViews": 9007199254740991,
"author": {
"id": "%s",
"name": "Test Author",
Expand Down Expand Up @@ -793,6 +796,7 @@ func requirePost(
isPublished
tags
numLikes
numViews
author @include(if: $getAuthor) {
id
name
Expand Down Expand Up @@ -1946,12 +1950,12 @@ func addStarship(t *testing.T) *starship {
gqlResponse := addStarshipParams.ExecuteAsPost(t, graphqlURL)
RequireNoGQLErrors(t, gqlResponse)

addStarshipExpected := fmt.Sprintf(`{"addStarship":{
addStarshipExpected := `{"addStarship":{
"starship":[{
"name":"Millennium Falcon",
"length":2
}]
}}`)
}}`

var expected, result struct {
AddStarship struct {
Expand Down
38 changes: 37 additions & 1 deletion graphql/e2e/common/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -663,7 +663,7 @@ func authorTest(t *testing.T, filter interface{}, expected []*author) {
}
}

func intFilters(t *testing.T) {
func int32Filters(t *testing.T) {
cases := map[string]struct {
Filter interface{}
Expected []*post
Expand Down Expand Up @@ -699,6 +699,42 @@ func intFilters(t *testing.T) {
}
}

func int64Filters(t *testing.T) {
cases := map[string]struct {
Filter interface{}
Expected []*post
}{
"less than": {
Filter: map[string]interface{}{"numViews": map[string]interface{}{"lt": 274877906944}},
Expected: []*post{
{Title: "GraphQL doco"},
{Title: "Random post"}}},
"less or equal": {
Filter: map[string]interface{}{"numViews": map[string]interface{}{"le": 274877906944}},
Expected: []*post{
{Title: "GraphQL doco"},
{Title: "Learning GraphQL in Dgraph"},
{Title: "Random post"}}},
"equal": {
Filter: map[string]interface{}{"numViews": map[string]interface{}{"eq": 274877906944}},
Expected: []*post{{Title: "Learning GraphQL in Dgraph"}}},
"greater or equal": {
Filter: map[string]interface{}{"numViews": map[string]interface{}{"ge": 274877906944}},
Expected: []*post{
{Title: "Introducing GraphQL in Dgraph"},
{Title: "Learning GraphQL in Dgraph"}}},
"greater than": {
Filter: map[string]interface{}{"numViews": map[string]interface{}{"gt": 274877906944}},
Expected: []*post{{Title: "Introducing GraphQL in Dgraph"}}},
}

for name, test := range cases {
t.Run(name, func(t *testing.T) {
postTest(t, test.Filter, test.Expected)
})
}
}

func booleanFilters(t *testing.T) {
cases := map[string]struct {
Filter interface{}
Expand Down
7 changes: 7 additions & 0 deletions graphql/e2e/directives/dgraph_directives_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,11 @@ func TestSchema_WithDgraphDirectives(t *testing.T) {
"type": "int",
"index": true,
"tokenizer": ["int"]
}, {
"predicate": "myPost.numViews",
"type": "int",
"index": true,
"tokenizer": ["int"]
}, {
"predicate": "myPost.postType",
"type": "string",
Expand Down Expand Up @@ -323,6 +328,8 @@ func TestSchema_WithDgraphDirectives(t *testing.T) {
"name": "test.dgraph.topic"
}, {
"name": "myPost.numLikes"
}, {
"name": "myPost.numViews"
}, {
"name": "is_published"
}, {
Expand Down
1 change: 1 addition & 0 deletions graphql/e2e/directives/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ type Post @dgraph(type: "myPost") {
tags: [String] @search(by: [exact])
topic: String @search(by: [exact]) @dgraph(pred: "test.dgraph.topic")
numLikes: Int @search
numViews: Int64 @search
isPublished: Boolean @search @dgraph(pred: "is_published")
postType: PostType @search(by: [hash, trigram])
author: Author! @hasInverse(field: posts) @dgraph(pred: "post.author")
Expand Down
4 changes: 4 additions & 0 deletions graphql/e2e/directives/test_data.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"myPost.tags": ["GraphQL", "Dgraph", "Database"],
"test.dgraph.topic": "GraphQL",
"myPost.numLikes": 100,
"myPost.numViews": 280000000000,
"is_published": true,
"myPost.postType": "Fact",
"post.author": { "uid": "_:author1" }
Expand All @@ -60,6 +61,7 @@
"myPost.tags": ["GraphQL", "Dgraph"],
"test.dgraph.topic": "Learn",
"myPost.numLikes": 87,
"myPost.numViews": 274877906944,
"is_published": true,
"myPost.postType": "Question",
"post.author": { "uid": "_:author2" }
Expand All @@ -72,6 +74,7 @@
"myPost.tags": ["GraphQL", "Dgraph"],
"test.dgraph.topic": "Docs",
"myPost.numLikes": 77,
"myPost.numViews": 2147483648,
"is_published": true,
"myPost.postType": "Opinion",
"post.author": { "uid": "_:author1" }
Expand All @@ -84,6 +87,7 @@
"myPost.tags": ["Random"],
"test.dgraph.topic": "Random",
"myPost.numLikes": 0,
"myPost.numViews": 0,
"is_published": false,
"myPost.postType": "Fact",
"post.author": { "uid": "_:author2" }
Expand Down
7 changes: 7 additions & 0 deletions graphql/e2e/normal/normal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,11 @@ func TestSchema_Normal(t *testing.T) {
"type": "int",
"index": true,
"tokenizer": ["int"]
}, {
"predicate": "Post.numViews",
"type": "int",
"index": true,
"tokenizer": ["int"]
}, {
"predicate": "Post.postType",
"type": "string",
Expand Down Expand Up @@ -309,6 +314,8 @@ func TestSchema_Normal(t *testing.T) {
"name": "Post.topic"
}, {
"name": "Post.numLikes"
}, {
"name": "Post.numViews"
}, {
"name": "Post.isPublished"
}, {
Expand Down
1 change: 1 addition & 0 deletions graphql/e2e/normal/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ type Post {
tags: [String] @search(by: [exact])
topic: String @search(by: [exact])
numLikes: Int @search
numViews: Int64 @search
isPublished: Boolean @search
postType: PostType @search(by: [hash, trigram])
author: Author! @hasInverse(field: posts)
Expand Down
4 changes: 4 additions & 0 deletions graphql/e2e/normal/test_data.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"Post.tags": ["GraphQL", "Dgraph", "Database"],
"Post.topic": "GraphQL",
"Post.numLikes": 100,
"Post.numViews": 280000000000,
"Post.isPublished": true,
"Post.postType": "Fact",
"Post.author": { "uid": "_:author1" }
Expand All @@ -60,6 +61,7 @@
"Post.tags": ["GraphQL", "Dgraph"],
"Post.topic": "Learn",
"Post.numLikes": 87,
"Post.numViews": 274877906944,
"Post.isPublished": true,
"Post.postType": "Question",
"Post.author": { "uid": "_:author2" }
Expand All @@ -72,6 +74,7 @@
"Post.tags": ["GraphQL", "Dgraph"],
"Post.topic": "Docs",
"Post.numLikes": 77,
"Post.numViews": 2147483648,
"Post.isPublished": true,
"Post.postType": "Opinion",
"Post.author": { "uid": "_:author1" }
Expand All @@ -84,6 +87,7 @@
"Post.tags": ["Random"],
"Post.topic": "Random",
"Post.numLikes": 0,
"Post.numViews": 0,
"Post.isPublished": false,
"Post.postType": "Fact",
"Post.author": { "uid": "_:author2" }
Expand Down
77 changes: 58 additions & 19 deletions graphql/resolve/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -1348,7 +1348,7 @@ func completeValue(
switch val := val.(type) {
case map[string]interface{}:
switch field.Type().Name() {
case "String", "ID", "Boolean", "Float", "Int", "DateTime":
case "String", "ID", "Boolean", "Float", "Int", "Int64", "DateTime":
return nil, x.GqlErrorList{&x.GqlError{
Message: errExpectedScalar,
Locations: []x.Location{field.Location()},
Expand Down Expand Up @@ -1477,15 +1477,13 @@ func coerceScalar(val interface{}, field schema.Field, path []interface{}) (inte
case float64:
// The spec says that we can coerce a Float value to Int, if we don't lose information.
// See: https: //spec.graphql.org/June2018/#sec-Float
// Lets try to see if this a whole number, otherwise return error because we
// might be losing informating by truncating it.
truncated := math.Trunc(v)
if truncated == v {
tv := int(truncated)
if tv > math.MaxInt32 || tv < math.MinInt32 {
return nil, valueCoercionError(v)
}
val = tv
// Lets try to see if this number could be converted to int32 without losing
// information, otherwise return error.
// See: https://github.com/golang/go/issues/19405 to understand why the comparison
// should be done after double conversion.
i32Val := int32(v)
if v == float64(i32Val) {
val = i32Val
} else {
return nil, valueCoercionError(v)
}
Expand All @@ -1496,17 +1494,17 @@ func coerceScalar(val interface{}, field schema.Field, path []interface{}) (inte
val = 0
}
case string:
i, err := strconv.ParseFloat(v, 32)
i, err := strconv.ParseFloat(v, 64)
// An error can be encountered if we had a value that can't be fit into
// a 32 bit floating point number.
// a 64 bit floating point number.
if err != nil {
return nil, valueCoercionError(v)
}
// Lets try to see if this a whole number, otherwise return error because we
// might be losing informating by truncating it.
truncated := math.Trunc(i)
if truncated == i {
val = int(truncated)
// Lets try to see if this number could be converted to int32 without losing
// information, otherwise return error.
i32Val := int32(i)
if i == float64(i32Val) {
val = i32Val
} else {
return nil, valueCoercionError(v)
}
Expand All @@ -1516,14 +1514,55 @@ func coerceScalar(val interface{}, field schema.Field, path []interface{}) (inte
}
case int:
// numUids are added as int, so we need special handling for that. Other number values
// in a JSON object are automatically unmarshaled as float so they are handle above.
// in a JSON object are automatically unmarshalled as float so they are handle above.
if v > math.MaxInt32 || v < math.MinInt32 {
return nil, valueCoercionError(v)
}
default:
return nil, valueCoercionError(v)
}

case "Int64":
switch v := val.(type) {
case float64:
// The spec says that we can coerce a Float value to Int, if we don't lose information.
// See: https: //spec.graphql.org/June2018/#sec-Float
// See: JSON RFC https://tools.ietf.org/html/rfc8259#section-6, to understand how the
// number type guarantees the correctness of integers only between the range
// [-(2**53)+1, (2**53)-1] and not the range [-(2**63), (2**63)-1].
// Lets try to see if this number could be converted to int64 without losing
// information, otherwise return error.
// See: https://github.com/golang/go/issues/19405 to understand why the comparison
// should be done after double conversion.
i64Val := int64(v)
if v == float64(i64Val) {
val = i64Val
} else {
return nil, valueCoercionError(v)
}
case bool:
if v {
val = 1
} else {
val = 0
}
case string:
i, err := strconv.ParseFloat(v, 64)
// An error can be encountered if we had a value that can't be fit into
// a 64 bit floating point number.
if err != nil {
return nil, valueCoercionError(v)
}
// Lets try to see if this number could be converted to int64 without losing
// information, otherwise return error.
i64Val := int64(i)
if i == float64(i64Val) {
val = i64Val
} else {
return nil, valueCoercionError(v)
}
default:
return nil, valueCoercionError(v)
}
case "Float":
switch v := val.(type) {
case bool:
Expand Down
Loading