From 858673b81f3549c4fe12bd03e9e55c2d80fce476 Mon Sep 17 00:00:00 2001 From: Mathew Byrne Date: Mon, 1 Oct 2018 11:38:52 +1000 Subject: [PATCH 1/2] Added reproduce test case --- codegen/testserver/generated.go | 104 ++++++++++++++++++++++++++++++ codegen/testserver/gqlgen.yml | 2 + codegen/testserver/models.go | 9 +++ codegen/testserver/resolver.go | 9 +++ codegen/testserver/schema.graphql | 5 ++ 5 files changed, 129 insertions(+) diff --git a/codegen/testserver/generated.go b/codegen/testserver/generated.go index 2c64cd9f4a3..92c2468b234 100644 --- a/codegen/testserver/generated.go +++ b/codegen/testserver/generated.go @@ -33,6 +33,7 @@ type Config struct { } type ResolverRoot interface { + EmbeddedPointer() EmbeddedPointerResolver ForcedResolver() ForcedResolverResolver Query() QueryResolver Subscription() SubscriptionResolver @@ -47,6 +48,11 @@ type ComplexityRoot struct { Area func(childComplexity int) int } + EmbeddedPointer struct { + Id func(childComplexity int) int + Title func(childComplexity int) int + } + Error struct { Id func(childComplexity int) int ErrorOnNonRequiredField func(childComplexity int) int @@ -100,6 +106,9 @@ type ComplexityRoot struct { } } +type EmbeddedPointerResolver interface { + Title(ctx context.Context, obj *EmbeddedPointerModel) (*string, error) +} type ForcedResolverResolver interface { Field(ctx context.Context, obj *ForcedResolver) (*Circle, error) } @@ -525,6 +534,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Circle.Area(childComplexity), true + case "EmbeddedPointer.ID": + if e.complexity.EmbeddedPointer.Id == nil { + break + } + + return e.complexity.EmbeddedPointer.Id(childComplexity), true + + case "EmbeddedPointer.Title": + if e.complexity.EmbeddedPointer.Title == nil { + break + } + + return e.complexity.EmbeddedPointer.Title(childComplexity), true + case "Error.id": if e.complexity.Error.Id == nil { break @@ -854,6 +877,82 @@ func (ec *executionContext) _Circle_area(ctx context.Context, field graphql.Coll return graphql.MarshalFloat(res) } +var embeddedPointerImplementors = []string{"EmbeddedPointer"} + +// nolint: gocyclo, errcheck, gas, goconst +func (ec *executionContext) _EmbeddedPointer(ctx context.Context, sel ast.SelectionSet, obj *EmbeddedPointerModel) graphql.Marshaler { + fields := graphql.CollectFields(ctx, sel, embeddedPointerImplementors) + + var wg sync.WaitGroup + out := graphql.NewOrderedMap(len(fields)) + invalid := false + for i, field := range fields { + out.Keys[i] = field.Alias + + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("EmbeddedPointer") + case "ID": + out.Values[i] = ec._EmbeddedPointer_ID(ctx, field, obj) + case "Title": + wg.Add(1) + go func(i int, field graphql.CollectedField) { + out.Values[i] = ec._EmbeddedPointer_Title(ctx, field, obj) + wg.Done() + }(i, field) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + wg.Wait() + if invalid { + return graphql.Null + } + return out +} + +// nolint: vetshadow +func (ec *executionContext) _EmbeddedPointer_ID(ctx context.Context, field graphql.CollectedField, obj *EmbeddedPointerModel) graphql.Marshaler { + rctx := &graphql.ResolverContext{ + Object: "EmbeddedPointer", + Args: nil, + Field: field, + } + ctx = graphql.WithResolverContext(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(ctx context.Context) (interface{}, error) { + return obj.ID, nil + }) + if resTmp == nil { + return graphql.Null + } + res := resTmp.(string) + rctx.Result = res + return graphql.MarshalString(res) +} + +// nolint: vetshadow +func (ec *executionContext) _EmbeddedPointer_Title(ctx context.Context, field graphql.CollectedField, obj *EmbeddedPointerModel) graphql.Marshaler { + rctx := &graphql.ResolverContext{ + Object: "EmbeddedPointer", + Args: nil, + Field: field, + } + ctx = graphql.WithResolverContext(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(ctx context.Context) (interface{}, error) { + return ec.resolvers.EmbeddedPointer().Title(ctx, obj) + }) + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + rctx.Result = res + + if res == nil { + return graphql.Null + } + return graphql.MarshalString(*res) +} + var errorImplementors = []string{"Error"} // nolint: gocyclo, errcheck, gas, goconst @@ -3634,5 +3733,10 @@ union ShapeUnion = Circle | Rectangle type ForcedResolver { field: Circle } + +type EmbeddedPointer { + ID: String + Title: String +} `}, ) diff --git a/codegen/testserver/gqlgen.yml b/codegen/testserver/gqlgen.yml index 6cea9a88938..9f012f2a5dd 100644 --- a/codegen/testserver/gqlgen.yml +++ b/codegen/testserver/gqlgen.yml @@ -31,3 +31,5 @@ models: field: { resolver: true } Error: model: "github.com/99designs/gqlgen/codegen/testserver.Error" + EmbeddedPointer: + model: "github.com/99designs/gqlgen/codegen/testserver.EmbeddedPointerModel" diff --git a/codegen/testserver/models.go b/codegen/testserver/models.go index 0390ddb45dd..f8cb15fd25c 100644 --- a/codegen/testserver/models.go +++ b/codegen/testserver/models.go @@ -21,3 +21,12 @@ func (Error) ErrorOnNonRequiredField() (string, error) { func (Error) NilOnRequiredField() *string { return nil } + +type EmbeddedPointerModel struct { + *EmbeddedPointer + ID string +} + +type EmbeddedPointer struct { + Title string +} diff --git a/codegen/testserver/resolver.go b/codegen/testserver/resolver.go index 167a0c2825a..9290719ae58 100644 --- a/codegen/testserver/resolver.go +++ b/codegen/testserver/resolver.go @@ -11,6 +11,9 @@ import ( type Resolver struct{} +func (r *Resolver) EmbeddedPointer() EmbeddedPointerResolver { + return &embeddedPointerResolver{r} +} func (r *Resolver) ForcedResolver() ForcedResolverResolver { return &forcedResolverResolver{r} } @@ -21,6 +24,12 @@ func (r *Resolver) Subscription() SubscriptionResolver { return &subscriptionResolver{r} } +type embeddedPointerResolver struct{ *Resolver } + +func (r *embeddedPointerResolver) Title(ctx context.Context, obj *EmbeddedPointerModel) (*string, error) { + panic("not implemented") +} + type forcedResolverResolver struct{ *Resolver } func (r *forcedResolverResolver) Field(ctx context.Context, obj *ForcedResolver) (*Circle, error) { diff --git a/codegen/testserver/schema.graphql b/codegen/testserver/schema.graphql index d88aa2ee84f..7d623b5fe09 100644 --- a/codegen/testserver/schema.graphql +++ b/codegen/testserver/schema.graphql @@ -131,3 +131,8 @@ union ShapeUnion = Circle | Rectangle type ForcedResolver { field: Circle } + +type EmbeddedPointer { + ID: String + Title: String +} From 3db30daccd52a4d5f94ed9823d262d3089abaf44 Mon Sep 17 00:00:00 2001 From: Mathew Byrne Date: Mon, 1 Oct 2018 13:09:19 +1000 Subject: [PATCH 2/2] Check for embedded pointer when finding field on struct --- codegen/testserver/generated.go | 23 +++++------------------ codegen/testserver/resolver.go | 9 --------- codegen/util.go | 17 ++++++++--------- 3 files changed, 13 insertions(+), 36 deletions(-) diff --git a/codegen/testserver/generated.go b/codegen/testserver/generated.go index 92c2468b234..5bef4d92506 100644 --- a/codegen/testserver/generated.go +++ b/codegen/testserver/generated.go @@ -33,7 +33,6 @@ type Config struct { } type ResolverRoot interface { - EmbeddedPointer() EmbeddedPointerResolver ForcedResolver() ForcedResolverResolver Query() QueryResolver Subscription() SubscriptionResolver @@ -106,9 +105,6 @@ type ComplexityRoot struct { } } -type EmbeddedPointerResolver interface { - Title(ctx context.Context, obj *EmbeddedPointerModel) (*string, error) -} type ForcedResolverResolver interface { Field(ctx context.Context, obj *ForcedResolver) (*Circle, error) } @@ -883,7 +879,6 @@ var embeddedPointerImplementors = []string{"EmbeddedPointer"} func (ec *executionContext) _EmbeddedPointer(ctx context.Context, sel ast.SelectionSet, obj *EmbeddedPointerModel) graphql.Marshaler { fields := graphql.CollectFields(ctx, sel, embeddedPointerImplementors) - var wg sync.WaitGroup out := graphql.NewOrderedMap(len(fields)) invalid := false for i, field := range fields { @@ -895,16 +890,12 @@ func (ec *executionContext) _EmbeddedPointer(ctx context.Context, sel ast.Select case "ID": out.Values[i] = ec._EmbeddedPointer_ID(ctx, field, obj) case "Title": - wg.Add(1) - go func(i int, field graphql.CollectedField) { - out.Values[i] = ec._EmbeddedPointer_Title(ctx, field, obj) - wg.Done() - }(i, field) + out.Values[i] = ec._EmbeddedPointer_Title(ctx, field, obj) default: panic("unknown field " + strconv.Quote(field.Name)) } } - wg.Wait() + if invalid { return graphql.Null } @@ -939,18 +930,14 @@ func (ec *executionContext) _EmbeddedPointer_Title(ctx context.Context, field gr } ctx = graphql.WithResolverContext(ctx, rctx) resTmp := ec.FieldMiddleware(ctx, obj, func(ctx context.Context) (interface{}, error) { - return ec.resolvers.EmbeddedPointer().Title(ctx, obj) + return obj.Title, nil }) if resTmp == nil { return graphql.Null } - res := resTmp.(*string) + res := resTmp.(string) rctx.Result = res - - if res == nil { - return graphql.Null - } - return graphql.MarshalString(*res) + return graphql.MarshalString(res) } var errorImplementors = []string{"Error"} diff --git a/codegen/testserver/resolver.go b/codegen/testserver/resolver.go index 9290719ae58..167a0c2825a 100644 --- a/codegen/testserver/resolver.go +++ b/codegen/testserver/resolver.go @@ -11,9 +11,6 @@ import ( type Resolver struct{} -func (r *Resolver) EmbeddedPointer() EmbeddedPointerResolver { - return &embeddedPointerResolver{r} -} func (r *Resolver) ForcedResolver() ForcedResolverResolver { return &forcedResolverResolver{r} } @@ -24,12 +21,6 @@ func (r *Resolver) Subscription() SubscriptionResolver { return &subscriptionResolver{r} } -type embeddedPointerResolver struct{ *Resolver } - -func (r *embeddedPointerResolver) Title(ctx context.Context, obj *EmbeddedPointerModel) (*string, error) { - panic("not implemented") -} - type forcedResolverResolver struct{ *Resolver } func (r *forcedResolverResolver) Field(ctx context.Context, obj *ForcedResolver) (*Circle, error) { diff --git a/codegen/util.go b/codegen/util.go index 1849f100bb1..12530cb0b6a 100644 --- a/codegen/util.go +++ b/codegen/util.go @@ -132,17 +132,16 @@ func findField(typ *types.Struct, name, structTag string) (*types.Var, error) { } if field.Anonymous() { - if named, ok := field.Type().(*types.Struct); ok { - f, err := findField(named, name, structTag) - if err != nil && !strings.HasPrefix(err.Error(), "no field named") { - return nil, err - } - if f != nil && foundField == nil { - foundField = f - } + + fieldType := field.Type() + + if ptr, ok := fieldType.(*types.Pointer); ok { + fieldType = ptr.Elem() } - if named, ok := field.Type().Underlying().(*types.Struct); ok { + // Type.Underlying() returns itself for all types except types.Named, where it returns a struct type. + // It should be safe to always call. + if named, ok := fieldType.Underlying().(*types.Struct); ok { f, err := findField(named, name, structTag) if err != nil && !strings.HasPrefix(err.Error(), "no field named") { return nil, err