From d2265f3d0a40525359328a52a3d30467f330baa5 Mon Sep 17 00:00:00 2001 From: Adam Scarr Date: Thu, 2 Aug 2018 12:01:42 +1000 Subject: [PATCH] Add implicit value to array coercion --- codegen/type.go | 2 + example/dataloader/dataloader_test.go | 38 +++++++++++-- example/dataloader/generated.go | 80 ++++++++++++++++++++++++--- example/dataloader/resolvers.go | 11 +++- example/dataloader/schema.graphql | 5 +- 5 files changed, 120 insertions(+), 16 deletions(-) diff --git a/codegen/type.go b/codegen/type.go index 689f4e4aa34..04bb0954315 100644 --- a/codegen/type.go +++ b/codegen/type.go @@ -112,6 +112,8 @@ func (t Type) unmarshal(result, raw string, remainingMods []string, depth int) s if {{.raw}} != nil { if tmp1, ok := {{.raw}}.([]interface{}); ok { {{.rawSlice}} = tmp1 + } else { + {{.rawSlice}} = []interface{}{ {{.raw}} } } } {{.result}} = make({{.type}}, len({{.rawSlice}})) diff --git a/example/dataloader/dataloader_test.go b/example/dataloader/dataloader_test.go index f866f64af53..da418023936 100644 --- a/example/dataloader/dataloader_test.go +++ b/example/dataloader/dataloader_test.go @@ -34,16 +34,42 @@ func TestTodo(t *testing.T) { }`, &resp) }) - t.Run("customer array torture", func(t *testing.T) { + t.Run("2d array marshaling", func(t *testing.T) { var resp struct { - Torture [][]Customer + Torture2d [][]Customer } - c.MustPost(`{ torture(customerIds:[[1,2],[3,4,5]]) { id name } }`, &resp) + c.MustPost(`{ torture2d(customerIds:[[1,2],[3,4,5]]) { id name } }`, &resp) require.EqualValues(t, [][]Customer{ {{ID: 1, Name: "0 0"}, {ID: 2, Name: "0 1"}}, {{ID: 3, Name: "1 0"}, {ID: 4, Name: "1 1"}, {ID: 5, Name: "1 2"}}, - }, resp.Torture) + }, resp.Torture2d) + }) + + // Input coercion on arrays should convert non array values into an array of the appropriate depth + // http://facebook.github.io/graphql/June2018/#sec-Type-System.List + t.Run("array coercion", func(t *testing.T) { + t.Run("1d", func(t *testing.T) { + var resp struct { + Torture1d []Customer + } + c.MustPost(`{ torture1d(customerIds: 1) { id name } }`, &resp) + + require.EqualValues(t, []Customer{ + {ID: 1, Name: "0"}, + }, resp.Torture1d) + }) + + t.Run("2d", func(t *testing.T) { + var resp struct { + Torture2d [][]Customer + } + c.MustPost(`{ torture2d(customerIds: 1) { id name } }`, &resp) + + require.EqualValues(t, [][]Customer{ + {{ID: 1, Name: "0 0"}}, + }, resp.Torture2d) + }) }) t.Run("introspection", func(t *testing.T) { @@ -56,9 +82,9 @@ func TestTodo(t *testing.T) { var resp struct { Torture [][]Customer } - c.MustPost(`{ torture(customerIds:{}) { id name } }`, &resp) + err := c.Post(`{ torture2d(customerIds:{}) { id name } }`, &resp) - require.EqualValues(t, [][]Customer{}, resp.Torture) + require.EqualError(t, err, "[{\"message\":\"map[string]interface {} is not an int\"}]") }) } diff --git a/example/dataloader/generated.go b/example/dataloader/generated.go index ec67bef75b5..c0ee02bbdd3 100644 --- a/example/dataloader/generated.go +++ b/example/dataloader/generated.go @@ -44,7 +44,8 @@ type OrderResolver interface { } type QueryResolver interface { Customers(ctx context.Context) ([]Customer, error) - Torture(ctx context.Context, customerIds [][]int) ([][]Customer, error) + Torture1d(ctx context.Context, customerIds []int) ([]Customer, error) + Torture2d(ctx context.Context, customerIds [][]int) ([][]Customer, error) } type executableSchema struct { @@ -464,8 +465,10 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr out.Values[i] = graphql.MarshalString("Query") case "customers": out.Values[i] = ec._Query_customers(ctx, field) - case "torture": - out.Values[i] = ec._Query_torture(ctx, field) + case "torture1d": + out.Values[i] = ec._Query_torture1d(ctx, field) + case "torture2d": + out.Values[i] = ec._Query_torture2d(ctx, field) case "__type": out.Values[i] = ec._Query___type(ctx, field) case "__schema": @@ -513,7 +516,65 @@ func (ec *executionContext) _Query_customers(ctx context.Context, field graphql. }) } -func (ec *executionContext) _Query_torture(ctx context.Context, field graphql.CollectedField) graphql.Marshaler { +func (ec *executionContext) _Query_torture1d(ctx context.Context, field graphql.CollectedField) graphql.Marshaler { + rawArgs := field.ArgumentMap(ec.Variables) + args := map[string]interface{}{} + var arg0 []int + if tmp, ok := rawArgs["customerIds"]; ok { + var err error + var rawIf1 []interface{} + if tmp != nil { + if tmp1, ok := tmp.([]interface{}); ok { + rawIf1 = tmp1 + } else { + rawIf1 = []interface{}{tmp} + } + } + arg0 = make([]int, len(rawIf1)) + for idx1 := range rawIf1 { + arg0[idx1], err = graphql.UnmarshalInt(rawIf1[idx1]) + } + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + } + args["customerIds"] = arg0 + ctx = graphql.WithResolverContext(ctx, &graphql.ResolverContext{ + Object: "Query", + Args: args, + Field: field, + }) + return graphql.Defer(func() (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + userErr := ec.Recover(ctx, r) + ec.Error(ctx, userErr) + ret = graphql.Null + } + }() + + resTmp := ec.FieldMiddleware(ctx, func(ctx context.Context) (interface{}, error) { + return ec.resolvers.Query().Torture1d(ctx, args["customerIds"].([]int)) + }) + if resTmp == nil { + return graphql.Null + } + res := resTmp.([]Customer) + arr1 := graphql.Array{} + for idx1 := range res { + arr1 = append(arr1, func() graphql.Marshaler { + rctx := graphql.GetResolverContext(ctx) + rctx.PushIndex(idx1) + defer rctx.Pop() + return ec._Customer(ctx, field.Selections, &res[idx1]) + }()) + } + return arr1 + }) +} + +func (ec *executionContext) _Query_torture2d(ctx context.Context, field graphql.CollectedField) graphql.Marshaler { rawArgs := field.ArgumentMap(ec.Variables) args := map[string]interface{}{} var arg0 [][]int @@ -523,6 +584,8 @@ func (ec *executionContext) _Query_torture(ctx context.Context, field graphql.Co if tmp != nil { if tmp1, ok := tmp.([]interface{}); ok { rawIf1 = tmp1 + } else { + rawIf1 = []interface{}{tmp} } } arg0 = make([][]int, len(rawIf1)) @@ -531,6 +594,8 @@ func (ec *executionContext) _Query_torture(ctx context.Context, field graphql.Co if rawIf1[idx1] != nil { if tmp1, ok := rawIf1[idx1].([]interface{}); ok { rawIf2 = tmp1 + } else { + rawIf2 = []interface{}{rawIf1[idx1]} } } arg0[idx1] = make([]int, len(rawIf2)) @@ -559,7 +624,7 @@ func (ec *executionContext) _Query_torture(ctx context.Context, field graphql.Co }() resTmp := ec.FieldMiddleware(ctx, func(ctx context.Context) (interface{}, error) { - return ec.resolvers.Query().Torture(ctx, args["customerIds"].([][]int)) + return ec.resolvers.Query().Torture2d(ctx, args["customerIds"].([][]int)) }) if resTmp == nil { return graphql.Null @@ -1526,8 +1591,9 @@ var parsedSchema = gqlparser.MustLoadSchema( &ast.Source{Name: "schema.graphql", Input: `type Query { customers: [Customer!] - # this method is here to test code generation of nested arrays - torture(customerIds: [[Int!]]): [[Customer!]] + # these methods are here to test code generation of nested arrays + torture1d(customerIds: [Int!]): [Customer!] + torture2d(customerIds: [[Int!]]): [[Customer!]] } type Customer { diff --git a/example/dataloader/resolvers.go b/example/dataloader/resolvers.go index 77765e03bb4..1011adc3800 100644 --- a/example/dataloader/resolvers.go +++ b/example/dataloader/resolvers.go @@ -65,7 +65,16 @@ func (r *queryResolver) Customers(ctx context.Context) ([]Customer, error) { } // this method is here to test code generation of nested arrays -func (r *queryResolver) Torture(ctx context.Context, customerIds [][]int) ([][]Customer, error) { +func (r *queryResolver) Torture1d(ctx context.Context, customerIds []int) ([]Customer, error) { + result := make([]Customer, len(customerIds)) + for i, id := range customerIds { + result[i] = Customer{ID: id, Name: fmt.Sprintf("%d", i), AddressID: rand.Int() % 10} + } + return result, nil +} + +// this method is here to test code generation of nested arrays +func (r *queryResolver) Torture2d(ctx context.Context, customerIds [][]int) ([][]Customer, error) { result := make([][]Customer, len(customerIds)) for i := range customerIds { inner := make([]Customer, len(customerIds[i])) diff --git a/example/dataloader/schema.graphql b/example/dataloader/schema.graphql index dacddf47dd8..a0b579a36c2 100644 --- a/example/dataloader/schema.graphql +++ b/example/dataloader/schema.graphql @@ -1,8 +1,9 @@ type Query { customers: [Customer!] - # this method is here to test code generation of nested arrays - torture(customerIds: [[Int!]]): [[Customer!]] + # these methods are here to test code generation of nested arrays + torture1d(customerIds: [Int!]): [Customer!] + torture2d(customerIds: [[Int!]]): [[Customer!]] } type Customer {