diff --git a/.gitignore b/.gitignore index 2c5f0421a0..8b8a4a136b 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,4 @@ /docs/public /example/chat/node_modules /example/chat/package-lock.json -/codegen/tests/gen +/codegen/testdata/gen diff --git a/codegen/codegen.go b/codegen/codegen.go index 26a1eefb19..b6900ba5a6 100644 --- a/codegen/codegen.go +++ b/codegen/codegen.go @@ -93,6 +93,7 @@ func (cfg *Config) normalize() error { if cfg.ModelPackageName == "" { cfg.ModelPackageName = filepath.Base(cfg.modelDir) } + cfg.ModelPackageName = sanitizePackageName(cfg.ModelPackageName) cfg.modelPackagePath = fullPackageName(cfg.modelDir, cfg.ModelPackageName) if cfg.ExecFilename == "" { @@ -103,6 +104,7 @@ func (cfg *Config) normalize() error { if cfg.ExecPackageName == "" { cfg.ExecPackageName = filepath.Base(cfg.execDir) } + cfg.ExecPackageName = sanitizePackageName(cfg.ExecPackageName) cfg.execPackagePath = fullPackageName(cfg.execDir, cfg.ExecPackageName) builtins := map[string]string{ diff --git a/codegen/import_build.go b/codegen/import_build.go index 657511e93c..f917a188db 100644 --- a/codegen/import_build.go +++ b/codegen/import_build.go @@ -32,6 +32,10 @@ func buildImports(types NamedTypes, destDir string) Imports { var invalidPackageNameChar = regexp.MustCompile(`[^\w]`) +func sanitizePackageName(pkg string) string { + return invalidPackageNameChar.ReplaceAllLiteralString(filepath.Base(pkg), "_") +} + func (s Imports) addPkg(types NamedTypes, destDir string, pkg string) (Imports, *Import) { if pkg == "" { return s, nil @@ -43,11 +47,11 @@ func (s Imports) addPkg(types NamedTypes, destDir string, pkg string) (Imports, localName := "" if !strings.HasSuffix(destDir, pkg) { - localName = invalidPackageNameChar.ReplaceAllLiteralString(filepath.Base(pkg), "_") + localName = sanitizePackageName(filepath.Base(pkg)) i := 1 imp := s.findByName(localName) for imp != nil && imp.Package != pkg { - localName = invalidPackageNameChar.ReplaceAllLiteralString(filepath.Base(pkg), "_") + strconv.Itoa(i) + localName = sanitizePackageName(filepath.Base(pkg)) + strconv.Itoa(i) imp = s.findByName(localName) i++ if i > 10 { diff --git a/codegen/import_test.go b/codegen/import_test.go new file mode 100644 index 0000000000..3a0704f07e --- /dev/null +++ b/codegen/import_test.go @@ -0,0 +1,38 @@ +package codegen + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestInvalidPackagenames(t *testing.T) { + err := generate("invalid-packagename", ` + type Query { + invalidIdentifier: InvalidIdentifier + } + type InvalidIdentifier { + id: Int! + } + `, map[string]string{ + "InvalidIdentifier": "github.com/vektah/gqlgen/codegen/testdata/invalid-packagename.InvalidIdentifier", + }) + + require.NoError(t, err) +} + +func TestImportCollisions(t *testing.T) { + err := generate("complexinput", ` + type Query { + collision: It + } + type It { + id: ID! + } + + `, map[string]string{ + "It": "github.com/vektah/gqlgen/codegen/testdata/introspection.It", + }) + + require.NoError(t, err) +} diff --git a/codegen/input_test.go b/codegen/input_test.go new file mode 100644 index 0000000000..f75d7250eb --- /dev/null +++ b/codegen/input_test.go @@ -0,0 +1,92 @@ +package codegen + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestTypeUnionAsInput(t *testing.T) { + err := generate("inputunion", ` + type Query { + addBookmark(b: Bookmarkable!): Boolean! + } + type Item {} + union Bookmarkable = Item + `) + + require.EqualError(t, err, "model plan failed: Bookmarkable! cannot be used as argument of Query.addBookmark. only input and scalar types are allowed") +} + +func TestTypeInInput(t *testing.T) { + err := generate("typeinput", ` + type Query { + addBookmark(b: BookmarkableInput!): Boolean! + } + type Item {} + input BookmarkableInput { + item: Item + } + `) + + require.EqualError(t, err, "model plan failed: Item cannot be used as a field of BookmarkableInput. only input and scalar types are allowed") +} + +func TestRawMapInputs(t *testing.T) { + err := generate("rawmap", ` + type Query { + mapInput(input: Changes): Boolean + } + input Changes { + a: Int + b: Int + } + `, map[string]string{ + "Changes": "map[string]interface{}", + }) + + require.NoError(t, err) +} + +func TestRecursiveInputType(t *testing.T) { + err := generate("recursiveinput", ` + type Query { + recursive(input: RecursiveInputSlice): Boolean + } + input RecursiveInputSlice { + self: [RecursiveInputSlice!] + } + `, map[string]string{ + "RecursiveInputSlice": "github.com/vektah/gqlgen/codegen/testdata.RecursiveInputSlice", + }) + + require.NoError(t, err) +} + +func TestComplexInputTypes(t *testing.T) { + err := generate("complexinput", ` + type Query { + nestedInputs(input: [[OuterInput]] = [[{inner: {id: 1}}]]): Boolean + nestedOutputs: [[OuterObject]] + } + input InnerInput { + id:Int! + } + + input OuterInput { + inner: InnerInput! + } + + type OuterObject { + inner: InnerObject! + } + + type InnerObject { + id: Int! + } + `, map[string]string{ + "Changes": "map[string]interface{}", + }) + + require.NoError(t, err) +} diff --git a/codegen/interface_test.go b/codegen/interface_test.go new file mode 100644 index 0000000000..97c74ff469 --- /dev/null +++ b/codegen/interface_test.go @@ -0,0 +1,59 @@ +package codegen + +import ( + "testing" + + "github.com/stretchr/testify/require" + "golang.org/x/tools/go/loader" +) + +func TestShapes(t *testing.T) { + err := generate("shapes", ` + type Query { + shapes: [Shape] + } + interface Shape { + area: Float + } + type Circle implements Shape { + radius: Float + area: Float + } + type Rectangle implements Shape { + length: Float + width: Float + area: Float + } + union ShapeUnion = Circle | Rectangle + `, map[string]string{ + "Shape": "github.com/vektah/gqlgen/codegen/testdata.Shape", + "ShapeUnion": "github.com/vektah/gqlgen/codegen/testdata.ShapeUnion", + "Circle": "github.com/vektah/gqlgen/codegen/testdata.Circle", + "Rectangle": "github.com/vektah/gqlgen/codegen/testdata.Rectangle", + }) + + require.NoError(t, err) + +} + +func generate(name string, schema string, typemap ...map[string]string) error { + cfg := Config{ + SchemaStr: schema, + ExecFilename: "testdata/gen/" + name + "/exec.go", + ModelFilename: "testdata/gen/" + name + "/model.go", + } + if len(typemap) > 0 { + cfg.Typemap = typemap[0] + } + err := Generate(cfg) + if err == nil { + conf := loader.Config{} + conf.Import("github.com/vektah/gqlgen/codegen/testdata/gen/" + name) + + _, err := conf.Load() + if err != nil { + panic(err) + } + } + return err +} diff --git a/codegen/testdata/element.go b/codegen/testdata/element.go new file mode 100644 index 0000000000..feaed69f41 --- /dev/null +++ b/codegen/testdata/element.go @@ -0,0 +1,31 @@ +package testdata + +import ( + "context" + "errors" + "time" +) + +type Element struct { + ID int +} + +type ElementResolver struct{} + +func (r *ElementResolver) Query_path(ctx context.Context) ([]Element, error) { + return []Element{{1}, {2}, {3}, {4}}, nil +} + +func (r *ElementResolver) Element_child(ctx context.Context, obj *Element) (Element, error) { + return Element{obj.ID * 10}, nil +} + +func (r *ElementResolver) Element_error(ctx context.Context, obj *Element, message *string) (bool, error) { + // A silly hack to make the result order stable + time.Sleep(time.Duration(obj.ID) * 10 * time.Millisecond) + + if message != nil { + return true, errors.New(*message) + } + return false, nil +} diff --git a/test/models.go b/codegen/testdata/interfaces.go similarity index 80% rename from test/models.go rename to codegen/testdata/interfaces.go index d86daec960..ead09b1608 100644 --- a/test/models.go +++ b/codegen/testdata/interfaces.go @@ -1,4 +1,4 @@ -package test +package testdata import "math" @@ -25,7 +25,3 @@ type Rectangle struct { func (r *Rectangle) Area() float64 { return r.Length * r.Width } - -type RecursiveInputSlice struct { - Self *[]*RecursiveInputSlice -} diff --git a/test/introspection/it.go b/codegen/testdata/introspection/it.go similarity index 100% rename from test/introspection/it.go rename to codegen/testdata/introspection/it.go diff --git a/test/invalid-identifier/invalid-identifier.go b/codegen/testdata/invalid-packagename/invalid-identifier.go similarity index 60% rename from test/invalid-identifier/invalid-identifier.go rename to codegen/testdata/invalid-packagename/invalid-identifier.go index 1b13723059..fe0feb7165 100644 --- a/test/invalid-identifier/invalid-identifier.go +++ b/codegen/testdata/invalid-packagename/invalid-identifier.go @@ -1,4 +1,4 @@ -package invalid_identifier +package invalid_packagename type InvalidIdentifier struct { ID int diff --git a/codegen/testdata/recursive.go b/codegen/testdata/recursive.go new file mode 100644 index 0000000000..4e70332f8f --- /dev/null +++ b/codegen/testdata/recursive.go @@ -0,0 +1,5 @@ +package testdata + +type RecursiveInputSlice struct { + Self *[]*RecursiveInputSlice +} diff --git a/codegen/tests/input_union_test.go b/codegen/tests/input_union_test.go deleted file mode 100644 index f20d702b39..0000000000 --- a/codegen/tests/input_union_test.go +++ /dev/null @@ -1,42 +0,0 @@ -package tests - -import ( - "testing" - - "github.com/stretchr/testify/require" - "github.com/vektah/gqlgen/codegen" -) - -func TestTypeUnionAsInput(t *testing.T) { - err := codegen.Generate(codegen.Config{ - SchemaStr: ` - type Query { - addBookmark(b: Bookmarkable!): Boolean! - } - type Item {} - union Bookmarkable = Item - `, - ExecFilename: "gen/inputunion/exec.go", - ModelFilename: "gen/inputunion/model.go", - }) - - require.EqualError(t, err, "model plan failed: Bookmarkable! cannot be used as argument of Query.addBookmark. only input and scalar types are allowed") -} - -func TestTypeInInput(t *testing.T) { - err := codegen.Generate(codegen.Config{ - SchemaStr: ` - type Query { - addBookmark(b: BookmarkableInput!): Boolean! - } - type Item {} - input BookmarkableInput { - item: Item - } - `, - ExecFilename: "gen/typeinput/exec.go", - ModelFilename: "gen/typeinput/model.go", - }) - - require.EqualError(t, err, "model plan failed: Item cannot be used as a field of BookmarkableInput. only input and scalar types are allowed") -} diff --git a/handler/stub.go b/handler/stub.go index 6378568026..46b27e4642 100644 --- a/handler/stub.go +++ b/handler/stub.go @@ -32,7 +32,7 @@ func (e *executableSchemaStub) Mutation(ctx context.Context, op *query.Operation func (e *executableSchemaStub) Subscription(ctx context.Context, op *query.Operation) func() *graphql.Response { return func() *graphql.Response { - time.Sleep(20 * time.Millisecond) + time.Sleep(50 * time.Millisecond) select { case <-ctx.Done(): return nil diff --git a/test/generated.go b/test/generated.go index b2fe8e26a2..a19f751bbf 100644 --- a/test/generated.go +++ b/test/generated.go @@ -5,16 +5,12 @@ package test import ( "bytes" context "context" - fmt "fmt" strconv "strconv" graphql "github.com/vektah/gqlgen/graphql" introspection "github.com/vektah/gqlgen/neelance/introspection" query "github.com/vektah/gqlgen/neelance/query" schema "github.com/vektah/gqlgen/neelance/schema" - introspection1 "github.com/vektah/gqlgen/test/introspection" - invalid_identifier "github.com/vektah/gqlgen/test/invalid-identifier" - models "github.com/vektah/gqlgen/test/models" ) func MakeExecutableSchema(resolvers Resolvers) graphql.ExecutableSchema { @@ -24,15 +20,6 @@ func MakeExecutableSchema(resolvers Resolvers) graphql.ExecutableSchema { type Resolvers interface { Element_child(ctx context.Context, obj *Element) (Element, error) Element_error(ctx context.Context, obj *Element, message *string) (bool, error) - - OuterObject_inner(ctx context.Context, obj *models.OuterObject) (models.InnerObject, error) - Query_nestedInputs(ctx context.Context, input [][]models.OuterInput) (*bool, error) - Query_nestedOutputs(ctx context.Context) ([][]models.OuterObject, error) - Query_shapes(ctx context.Context) ([]Shape, error) - Query_recursive(ctx context.Context, input *RecursiveInputSlice) (*bool, error) - Query_mapInput(ctx context.Context, input *map[string]interface{}) (*bool, error) - Query_collision(ctx context.Context) (*introspection1.It, error) - Query_invalidIdentifier(ctx context.Context) (*invalid_identifier.InvalidIdentifier, error) Query_path(ctx context.Context) ([]Element, error) } @@ -74,53 +61,6 @@ type executionContext struct { resolvers Resolvers } -var circleImplementors = []string{"Circle", "Shape"} - -// nolint: gocyclo, errcheck, gas, goconst -func (ec *executionContext) _Circle(ctx context.Context, sel []query.Selection, obj *Circle) graphql.Marshaler { - fields := graphql.CollectFields(ec.Doc, sel, circleImplementors, ec.Variables) - - out := graphql.NewOrderedMap(len(fields)) - for i, field := range fields { - out.Keys[i] = field.Alias - - switch field.Name { - case "__typename": - out.Values[i] = graphql.MarshalString("Circle") - case "radius": - out.Values[i] = ec._Circle_radius(ctx, field, obj) - case "area": - out.Values[i] = ec._Circle_area(ctx, field, obj) - default: - panic("unknown field " + strconv.Quote(field.Name)) - } - } - - return out -} - -func (ec *executionContext) _Circle_radius(ctx context.Context, field graphql.CollectedField, obj *Circle) graphql.Marshaler { - rctx := graphql.GetResolverContext(ctx) - rctx.Object = "Circle" - rctx.Args = nil - rctx.Field = field - rctx.PushField(field.Alias) - defer rctx.Pop() - res := obj.Radius - return graphql.MarshalFloat(res) -} - -func (ec *executionContext) _Circle_area(ctx context.Context, field graphql.CollectedField, obj *Circle) graphql.Marshaler { - rctx := graphql.GetResolverContext(ctx) - rctx.Object = "Circle" - rctx.Args = nil - rctx.Field = field - rctx.PushField(field.Alias) - defer rctx.Pop() - res := obj.Area() - return graphql.MarshalFloat(res) -} - var elementImplementors = []string{"Element"} // nolint: gocyclo, errcheck, gas, goconst @@ -222,161 +162,6 @@ func (ec *executionContext) _Element_error(ctx context.Context, field graphql.Co }) } -var innerObjectImplementors = []string{"InnerObject"} - -// nolint: gocyclo, errcheck, gas, goconst -func (ec *executionContext) _InnerObject(ctx context.Context, sel []query.Selection, obj *models.InnerObject) graphql.Marshaler { - fields := graphql.CollectFields(ec.Doc, sel, innerObjectImplementors, ec.Variables) - - out := graphql.NewOrderedMap(len(fields)) - for i, field := range fields { - out.Keys[i] = field.Alias - - switch field.Name { - case "__typename": - out.Values[i] = graphql.MarshalString("InnerObject") - case "id": - out.Values[i] = ec._InnerObject_id(ctx, field, obj) - default: - panic("unknown field " + strconv.Quote(field.Name)) - } - } - - return out -} - -func (ec *executionContext) _InnerObject_id(ctx context.Context, field graphql.CollectedField, obj *models.InnerObject) graphql.Marshaler { - rctx := graphql.GetResolverContext(ctx) - rctx.Object = "InnerObject" - rctx.Args = nil - rctx.Field = field - rctx.PushField(field.Alias) - defer rctx.Pop() - res := obj.ID - return graphql.MarshalInt(res) -} - -var invalidIdentifierImplementors = []string{"InvalidIdentifier"} - -// nolint: gocyclo, errcheck, gas, goconst -func (ec *executionContext) _InvalidIdentifier(ctx context.Context, sel []query.Selection, obj *invalid_identifier.InvalidIdentifier) graphql.Marshaler { - fields := graphql.CollectFields(ec.Doc, sel, invalidIdentifierImplementors, ec.Variables) - - out := graphql.NewOrderedMap(len(fields)) - for i, field := range fields { - out.Keys[i] = field.Alias - - switch field.Name { - case "__typename": - out.Values[i] = graphql.MarshalString("InvalidIdentifier") - case "id": - out.Values[i] = ec._InvalidIdentifier_id(ctx, field, obj) - default: - panic("unknown field " + strconv.Quote(field.Name)) - } - } - - return out -} - -func (ec *executionContext) _InvalidIdentifier_id(ctx context.Context, field graphql.CollectedField, obj *invalid_identifier.InvalidIdentifier) graphql.Marshaler { - rctx := graphql.GetResolverContext(ctx) - rctx.Object = "InvalidIdentifier" - rctx.Args = nil - rctx.Field = field - rctx.PushField(field.Alias) - defer rctx.Pop() - res := obj.ID - return graphql.MarshalInt(res) -} - -var itImplementors = []string{"It"} - -// nolint: gocyclo, errcheck, gas, goconst -func (ec *executionContext) _It(ctx context.Context, sel []query.Selection, obj *introspection1.It) graphql.Marshaler { - fields := graphql.CollectFields(ec.Doc, sel, itImplementors, ec.Variables) - - out := graphql.NewOrderedMap(len(fields)) - for i, field := range fields { - out.Keys[i] = field.Alias - - switch field.Name { - case "__typename": - out.Values[i] = graphql.MarshalString("It") - case "id": - out.Values[i] = ec._It_id(ctx, field, obj) - default: - panic("unknown field " + strconv.Quote(field.Name)) - } - } - - return out -} - -func (ec *executionContext) _It_id(ctx context.Context, field graphql.CollectedField, obj *introspection1.It) graphql.Marshaler { - rctx := graphql.GetResolverContext(ctx) - rctx.Object = "It" - rctx.Args = nil - rctx.Field = field - rctx.PushField(field.Alias) - defer rctx.Pop() - res := obj.ID - return graphql.MarshalID(res) -} - -var outerObjectImplementors = []string{"OuterObject"} - -// nolint: gocyclo, errcheck, gas, goconst -func (ec *executionContext) _OuterObject(ctx context.Context, sel []query.Selection, obj *models.OuterObject) graphql.Marshaler { - fields := graphql.CollectFields(ec.Doc, sel, outerObjectImplementors, ec.Variables) - - out := graphql.NewOrderedMap(len(fields)) - for i, field := range fields { - out.Keys[i] = field.Alias - - switch field.Name { - case "__typename": - out.Values[i] = graphql.MarshalString("OuterObject") - case "inner": - out.Values[i] = ec._OuterObject_inner(ctx, field, obj) - default: - panic("unknown field " + strconv.Quote(field.Name)) - } - } - - return out -} - -func (ec *executionContext) _OuterObject_inner(ctx context.Context, field graphql.CollectedField, obj *models.OuterObject) graphql.Marshaler { - ctx = graphql.WithResolverContext(ctx, &graphql.ResolverContext{ - Object: "OuterObject", - Args: nil, - 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, err := ec.ResolverMiddleware(ctx, func(ctx context.Context) (interface{}, error) { - return ec.resolvers.OuterObject_inner(ctx, obj) - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - return graphql.Null - } - res := resTmp.(models.InnerObject) - return ec._InnerObject(ctx, field.Selections, &res) - }) -} - var queryImplementors = []string{"Query"} // nolint: gocyclo, errcheck, gas, goconst @@ -394,20 +179,6 @@ func (ec *executionContext) _Query(ctx context.Context, sel []query.Selection) g switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("Query") - case "nestedInputs": - out.Values[i] = ec._Query_nestedInputs(ctx, field) - case "nestedOutputs": - out.Values[i] = ec._Query_nestedOutputs(ctx, field) - case "shapes": - out.Values[i] = ec._Query_shapes(ctx, field) - case "recursive": - out.Values[i] = ec._Query_recursive(ctx, field) - case "mapInput": - out.Values[i] = ec._Query_mapInput(ctx, field) - case "collision": - out.Values[i] = ec._Query_collision(ctx, field) - case "invalidIdentifier": - out.Values[i] = ec._Query_invalidIdentifier(ctx, field) case "path": out.Values[i] = ec._Query_path(ctx, field) case "__schema": @@ -422,326 +193,6 @@ func (ec *executionContext) _Query(ctx context.Context, sel []query.Selection) g return out } -func (ec *executionContext) _Query_nestedInputs(ctx context.Context, field graphql.CollectedField) graphql.Marshaler { - args := map[string]interface{}{} - var arg0 [][]models.OuterInput - if tmp, ok := field.Args["input"]; ok { - var err error - rawIf1 := tmp.([]interface{}) - arg0 = make([][]models.OuterInput, len(rawIf1)) - for idx1 := range rawIf1 { - rawIf2 := rawIf1[idx1].([]interface{}) - arg0[idx1] = make([]models.OuterInput, len(rawIf2)) - for idx2 := range rawIf2 { - arg0[idx1][idx2], err = UnmarshalOuterInput(rawIf2[idx2]) - } - } - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - } else { - var tmp interface{} = []interface{}{[]interface{}{map[string]interface{}{"inner": map[string]interface{}{"id": 1}}}} - var err error - rawIf1 := tmp.([]interface{}) - arg0 = make([][]models.OuterInput, len(rawIf1)) - for idx1 := range rawIf1 { - rawIf2 := rawIf1[idx1].([]interface{}) - arg0[idx1] = make([]models.OuterInput, len(rawIf2)) - for idx2 := range rawIf2 { - arg0[idx1][idx2], err = UnmarshalOuterInput(rawIf2[idx2]) - } - } - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - } - - args["input"] = 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, err := ec.ResolverMiddleware(ctx, func(ctx context.Context) (interface{}, error) { - return ec.resolvers.Query_nestedInputs(ctx, args["input"].([][]models.OuterInput)) - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - return graphql.Null - } - res := resTmp.(*bool) - if res == nil { - return graphql.Null - } - return graphql.MarshalBoolean(*res) - }) -} - -func (ec *executionContext) _Query_nestedOutputs(ctx context.Context, field graphql.CollectedField) graphql.Marshaler { - ctx = graphql.WithResolverContext(ctx, &graphql.ResolverContext{ - Object: "Query", - Args: nil, - 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, err := ec.ResolverMiddleware(ctx, func(ctx context.Context) (interface{}, error) { - return ec.resolvers.Query_nestedOutputs(ctx) - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - return graphql.Null - } - res := resTmp.([][]models.OuterObject) - arr1 := graphql.Array{} - for idx1 := range res { - arr1 = append(arr1, func() graphql.Marshaler { - rctx := graphql.GetResolverContext(ctx) - rctx.PushIndex(idx1) - defer rctx.Pop() - arr2 := graphql.Array{} - for idx2 := range res[idx1] { - arr2 = append(arr2, func() graphql.Marshaler { - rctx := graphql.GetResolverContext(ctx) - rctx.PushIndex(idx2) - defer rctx.Pop() - return ec._OuterObject(ctx, field.Selections, &res[idx1][idx2]) - }()) - } - return arr2 - }()) - } - return arr1 - }) -} - -func (ec *executionContext) _Query_shapes(ctx context.Context, field graphql.CollectedField) graphql.Marshaler { - ctx = graphql.WithResolverContext(ctx, &graphql.ResolverContext{ - Object: "Query", - Args: nil, - 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, err := ec.ResolverMiddleware(ctx, func(ctx context.Context) (interface{}, error) { - return ec.resolvers.Query_shapes(ctx) - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - return graphql.Null - } - res := resTmp.([]Shape) - 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._Shape(ctx, field.Selections, &res[idx1]) - }()) - } - return arr1 - }) -} - -func (ec *executionContext) _Query_recursive(ctx context.Context, field graphql.CollectedField) graphql.Marshaler { - args := map[string]interface{}{} - var arg0 *RecursiveInputSlice - if tmp, ok := field.Args["input"]; ok { - var err error - var ptr1 RecursiveInputSlice - if tmp != nil { - ptr1, err = UnmarshalRecursiveInputSlice(tmp) - arg0 = &ptr1 - } - - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - } - args["input"] = 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, err := ec.ResolverMiddleware(ctx, func(ctx context.Context) (interface{}, error) { - return ec.resolvers.Query_recursive(ctx, args["input"].(*RecursiveInputSlice)) - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - return graphql.Null - } - res := resTmp.(*bool) - if res == nil { - return graphql.Null - } - return graphql.MarshalBoolean(*res) - }) -} - -func (ec *executionContext) _Query_mapInput(ctx context.Context, field graphql.CollectedField) graphql.Marshaler { - args := map[string]interface{}{} - var arg0 *map[string]interface{} - if tmp, ok := field.Args["input"]; ok { - var err error - var ptr1 map[string]interface{} - if tmp != nil { - ptr1 = tmp.(map[string]interface{}) - arg0 = &ptr1 - } - - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - } - args["input"] = 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, err := ec.ResolverMiddleware(ctx, func(ctx context.Context) (interface{}, error) { - return ec.resolvers.Query_mapInput(ctx, args["input"].(*map[string]interface{})) - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - return graphql.Null - } - res := resTmp.(*bool) - if res == nil { - return graphql.Null - } - return graphql.MarshalBoolean(*res) - }) -} - -func (ec *executionContext) _Query_collision(ctx context.Context, field graphql.CollectedField) graphql.Marshaler { - ctx = graphql.WithResolverContext(ctx, &graphql.ResolverContext{ - Object: "Query", - Args: nil, - 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, err := ec.ResolverMiddleware(ctx, func(ctx context.Context) (interface{}, error) { - return ec.resolvers.Query_collision(ctx) - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - return graphql.Null - } - res := resTmp.(*introspection1.It) - if res == nil { - return graphql.Null - } - return ec._It(ctx, field.Selections, res) - }) -} - -func (ec *executionContext) _Query_invalidIdentifier(ctx context.Context, field graphql.CollectedField) graphql.Marshaler { - ctx = graphql.WithResolverContext(ctx, &graphql.ResolverContext{ - Object: "Query", - Args: nil, - 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, err := ec.ResolverMiddleware(ctx, func(ctx context.Context) (interface{}, error) { - return ec.resolvers.Query_invalidIdentifier(ctx) - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - return graphql.Null - } - res := resTmp.(*invalid_identifier.InvalidIdentifier) - if res == nil { - return graphql.Null - } - return ec._InvalidIdentifier(ctx, field.Selections, res) - }) -} - func (ec *executionContext) _Query_path(ctx context.Context, field graphql.CollectedField) graphql.Marshaler { ctx = graphql.WithResolverContext(ctx, &graphql.ResolverContext{ Object: "Query", @@ -820,66 +271,6 @@ func (ec *executionContext) _Query___type(ctx context.Context, field graphql.Col return ec.___Type(ctx, field.Selections, res) } -var rectangleImplementors = []string{"Rectangle", "Shape"} - -// nolint: gocyclo, errcheck, gas, goconst -func (ec *executionContext) _Rectangle(ctx context.Context, sel []query.Selection, obj *Rectangle) graphql.Marshaler { - fields := graphql.CollectFields(ec.Doc, sel, rectangleImplementors, ec.Variables) - - out := graphql.NewOrderedMap(len(fields)) - for i, field := range fields { - out.Keys[i] = field.Alias - - switch field.Name { - case "__typename": - out.Values[i] = graphql.MarshalString("Rectangle") - case "length": - out.Values[i] = ec._Rectangle_length(ctx, field, obj) - case "width": - out.Values[i] = ec._Rectangle_width(ctx, field, obj) - case "area": - out.Values[i] = ec._Rectangle_area(ctx, field, obj) - default: - panic("unknown field " + strconv.Quote(field.Name)) - } - } - - return out -} - -func (ec *executionContext) _Rectangle_length(ctx context.Context, field graphql.CollectedField, obj *Rectangle) graphql.Marshaler { - rctx := graphql.GetResolverContext(ctx) - rctx.Object = "Rectangle" - rctx.Args = nil - rctx.Field = field - rctx.PushField(field.Alias) - defer rctx.Pop() - res := obj.Length - return graphql.MarshalFloat(res) -} - -func (ec *executionContext) _Rectangle_width(ctx context.Context, field graphql.CollectedField, obj *Rectangle) graphql.Marshaler { - rctx := graphql.GetResolverContext(ctx) - rctx.Object = "Rectangle" - rctx.Args = nil - rctx.Field = field - rctx.PushField(field.Alias) - defer rctx.Pop() - res := obj.Width - return graphql.MarshalFloat(res) -} - -func (ec *executionContext) _Rectangle_area(ctx context.Context, field graphql.CollectedField, obj *Rectangle) graphql.Marshaler { - rctx := graphql.GetResolverContext(ctx) - rctx.Object = "Rectangle" - rctx.Args = nil - rctx.Field = field - rctx.PushField(field.Alias) - defer rctx.Pop() - res := obj.Area() - return graphql.MarshalFloat(res) -} - var __DirectiveImplementors = []string{"__Directive"} // nolint: gocyclo, errcheck, gas, goconst @@ -1606,96 +997,6 @@ func (ec *executionContext) ___Type_ofType(ctx context.Context, field graphql.Co return ec.___Type(ctx, field.Selections, res) } -func (ec *executionContext) _Shape(ctx context.Context, sel []query.Selection, obj *Shape) graphql.Marshaler { - switch obj := (*obj).(type) { - case nil: - return graphql.Null - case *Circle: - return ec._Circle(ctx, sel, obj) - case *Rectangle: - return ec._Rectangle(ctx, sel, obj) - default: - panic(fmt.Errorf("unexpected type %T", obj)) - } -} - -func (ec *executionContext) _ShapeUnion(ctx context.Context, sel []query.Selection, obj *ShapeUnion) graphql.Marshaler { - switch obj := (*obj).(type) { - case nil: - return graphql.Null - case *Circle: - return ec._Circle(ctx, sel, obj) - case *Rectangle: - return ec._Rectangle(ctx, sel, obj) - default: - panic(fmt.Errorf("unexpected type %T", obj)) - } -} - -func UnmarshalInnerInput(v interface{}) (models.InnerInput, error) { - var it models.InnerInput - - for k, v := range v.(map[string]interface{}) { - switch k { - case "id": - var err error - it.ID, err = graphql.UnmarshalInt(v) - if err != nil { - return it, err - } - } - } - - return it, nil -} - -func UnmarshalOuterInput(v interface{}) (models.OuterInput, error) { - var it models.OuterInput - - for k, v := range v.(map[string]interface{}) { - switch k { - case "inner": - var err error - it.Inner, err = UnmarshalInnerInput(v) - if err != nil { - return it, err - } - } - } - - return it, nil -} - -func UnmarshalRecursiveInputSlice(v interface{}) (RecursiveInputSlice, error) { - var it RecursiveInputSlice - - for k, v := range v.(map[string]interface{}) { - switch k { - case "self": - var err error - var ptr1 []*RecursiveInputSlice - if v != nil { - rawIf2 := v.([]interface{}) - ptr1 = make([]*RecursiveInputSlice, len(rawIf2)) - for idx2 := range rawIf2 { - var ptr3 RecursiveInputSlice - if rawIf2[idx2] != nil { - ptr3, err = UnmarshalRecursiveInputSlice(rawIf2[idx2]) - ptr1[idx2] = &ptr3 - } - } - it.Self = &ptr1 - } - - if err != nil { - return it, err - } - } - } - - return it, nil -} - func (ec *executionContext) introspectSchema() *introspection.Schema { return introspection.WrapSchema(parsedSchema) } @@ -1708,69 +1009,12 @@ func (ec *executionContext) introspectType(name string) *introspection.Type { return introspection.WrapType(t) } -var parsedSchema = schema.MustParse(`input InnerInput { - id:Int! -} - -input OuterInput { - inner: InnerInput! -} - -type OuterObject { - inner: InnerObject! -} - -type InnerObject { - id: Int! -} - -interface Shape { - area: Float -} - -type Circle implements Shape { - radius: Float - area: Float -} - -type Rectangle implements Shape { - length: Float - width: Float - area: Float -} - -input RecursiveInputSlice { - self: [RecursiveInputSlice!] -} - -union ShapeUnion = Circle | Rectangle - -input Changes { - a: Int - b: Int -} - -type It { - id: ID! -} - -type InvalidIdentifier { - id: Int! -} - -type Element { +var parsedSchema = schema.MustParse(`type Element { child: Element! error(message: String): Boolean! } type Query { - nestedInputs(input: [[OuterInput]] = [[{inner: {id: 1}}]]): Boolean - nestedOutputs: [[OuterObject]] - shapes: [Shape] - recursive(input: RecursiveInputSlice): Boolean - mapInput(input: Changes): Boolean - collision: It - invalidIdentifier: InvalidIdentifier path: [Element] } `) diff --git a/test/models/generated.go b/test/models/generated.go deleted file mode 100644 index c3de8caa5a..0000000000 --- a/test/models/generated.go +++ /dev/null @@ -1,16 +0,0 @@ -// This file was generated by github.com/vektah/gqlgen, DO NOT EDIT - -package models - -type InnerInput struct { - ID int -} -type InnerObject struct { - ID int -} -type OuterInput struct { - Inner InnerInput -} -type OuterObject struct { - InnerID int -} diff --git a/test/resolvers_test.go b/test/resolvers_test.go index 5891cdf34b..91f6849a64 100644 --- a/test/resolvers_test.go +++ b/test/resolvers_test.go @@ -4,7 +4,7 @@ package test import ( "context" - fmt "fmt" + "fmt" "net/http/httptest" "testing" "time" @@ -14,8 +14,6 @@ import ( "github.com/vektah/gqlgen/client" "github.com/vektah/gqlgen/graphql" "github.com/vektah/gqlgen/handler" - "github.com/vektah/gqlgen/test/introspection" - invalid_identifier "github.com/vektah/gqlgen/test/invalid-identifier" "github.com/vektah/gqlgen/test/models" ) @@ -32,73 +30,39 @@ func TestCustomErrorPresenter(t *testing.T) { c := client.New(srv.URL) t.Run("special error", func(t *testing.T) { - resolvers.nestedOutputsErr = &specialErr{} + resolvers.err = &specialErr{} var resp struct{} - err := c.Post(`query { nestedOutputs { inner { id } } }`, &resp) + err := c.Post(`{ path { cc:child { error } } }`, &resp) - assert.EqualError(t, err, `[{"message":"override special error message"}]`) + assert.EqualError(t, err, `[{"message":"override special error message"},{"message":"override special error message"},{"message":"override special error message"},{"message":"override special error message"}]`) }) t.Run("normal error", func(t *testing.T) { - resolvers.nestedOutputsErr = fmt.Errorf("a normal error") + resolvers.err = fmt.Errorf("a normal error") var resp struct{} - err := c.Post(`query { nestedOutputs { inner { id } } }`, &resp) + err := c.Post(`{ path { cc:child { error } } }`, &resp) - assert.EqualError(t, err, `[{"message":"a normal error"}]`) + assert.EqualError(t, err, `[{"message":"a normal error"},{"message":"a normal error"},{"message":"a normal error"},{"message":"a normal error"}]`) }) } func TestErrorPath(t *testing.T) { - srv := httptest.NewServer(handler.GraphQL(MakeExecutableSchema(&testResolvers{}))) + srv := httptest.NewServer(handler.GraphQL(MakeExecutableSchema(&testResolvers{fmt.Errorf("boom")}))) c := client.New(srv.URL) var resp struct{} - err := c.Post(`{ path { cc:child { error(message: "boom") } } }`, &resp) + err := c.Post(`{ path { cc:child { error } } }`, &resp) assert.EqualError(t, err, `[{"message":"boom","path":["path",0,"cc","error"]},{"message":"boom","path":["path",1,"cc","error"]},{"message":"boom","path":["path",2,"cc","error"]},{"message":"boom","path":["path",3,"cc","error"]}]`) } type testResolvers struct { - inner models.InnerObject - innerErr error - nestedInputs *bool - nestedInputsErr error - nestedOutputs [][]models.OuterObject - nestedOutputsErr error - invalidIdentifier *invalid_identifier.InvalidIdentifier + err error } -func (r *testResolvers) Query_shapes(ctx context.Context) ([]Shape, error) { +func (r *testResolvers) Query_inputDefaults(ctx context.Context, input *models.DateFilter) (*bool, error) { panic("implement me") } -func (r *testResolvers) Query_recursive(ctx context.Context, input *RecursiveInputSlice) (*bool, error) { - panic("implement me") -} - -func (r *testResolvers) Query_mapInput(ctx context.Context, input *map[string]interface{}) (*bool, error) { - panic("implement me") -} - -func (r *testResolvers) Query_collision(ctx context.Context) (*introspection.It, error) { - panic("implement me") -} - -func (r *testResolvers) OuterObject_inner(ctx context.Context, obj *models.OuterObject) (models.InnerObject, error) { - return r.inner, r.innerErr -} - -func (r *testResolvers) Query_nestedInputs(ctx context.Context, input [][]models.OuterInput) (*bool, error) { - return r.nestedInputs, r.nestedInputsErr -} - -func (r *testResolvers) Query_nestedOutputs(ctx context.Context) ([][]models.OuterObject, error) { - return r.nestedOutputs, r.nestedOutputsErr -} - -func (r *testResolvers) Query_invalidIdentifier(ctx context.Context) (*invalid_identifier.InvalidIdentifier, error) { - return r.invalidIdentifier, nil -} - func (r *testResolvers) Query_path(ctx context.Context) ([]Element, error) { return []Element{{1}, {2}, {3}, {4}}, nil } @@ -107,14 +71,11 @@ func (r *testResolvers) Element_child(ctx context.Context, obj *Element) (Elemen return Element{obj.ID * 10}, nil } -func (r *testResolvers) Element_error(ctx context.Context, obj *Element, message *string) (bool, error) { +func (r *testResolvers) Element_error(ctx context.Context, obj *Element) (bool, error) { // A silly hack to make the result order stable time.Sleep(time.Duration(obj.ID) * 10 * time.Millisecond) - if message != nil { - return true, errors.New(*message) - } - return false, nil + return false, r.err } type specialErr struct{} diff --git a/test/schema.graphql b/test/schema.graphql index 3bd64cb659..463a7dedc1 100644 --- a/test/schema.graphql +++ b/test/schema.graphql @@ -1,65 +1,8 @@ -input InnerInput { - id:Int! -} - -input OuterInput { - inner: InnerInput! -} - -type OuterObject { - inner: InnerObject! -} - -type InnerObject { - id: Int! -} - -interface Shape { - area: Float -} - -type Circle implements Shape { - radius: Float - area: Float -} - -type Rectangle implements Shape { - length: Float - width: Float - area: Float -} - -input RecursiveInputSlice { - self: [RecursiveInputSlice!] -} - -union ShapeUnion = Circle | Rectangle - -input Changes { - a: Int - b: Int -} - -type It { - id: ID! -} - -type InvalidIdentifier { - id: Int! -} - type Element { child: Element! error(message: String): Boolean! } type Query { - nestedInputs(input: [[OuterInput]] = [[{inner: {id: 1}}]]): Boolean - nestedOutputs: [[OuterObject]] - shapes: [Shape] - recursive(input: RecursiveInputSlice): Boolean - mapInput(input: Changes): Boolean - collision: It - invalidIdentifier: InvalidIdentifier path: [Element] } diff --git a/test/types.json b/test/types.json index 6d14290bb6..a21ec7a328 100644 --- a/test/types.json +++ b/test/types.json @@ -1,11 +1,3 @@ { - "Shape": "github.com/vektah/gqlgen/test.Shape", - "ShapeUnion": "github.com/vektah/gqlgen/test.ShapeUnion", - "Circle": "github.com/vektah/gqlgen/test.Circle", - "Rectangle": "github.com/vektah/gqlgen/test.Rectangle", - "RecursiveInputSlice": "github.com/vektah/gqlgen/test.RecursiveInputSlice", - "It": "github.com/vektah/gqlgen/test/introspection.It", - "Changes": "map[string]interface{}", - "InvalidIdentifier": "github.com/vektah/gqlgen/test/invalid-identifier.InvalidIdentifier", "Element": "github.com/vektah/gqlgen/test.Element" }