From 70e860cce0a3c943f34b796351cc956fa48ab900 Mon Sep 17 00:00:00 2001 From: Matias Anaya Date: Fri, 8 Nov 2019 19:47:46 +1100 Subject: [PATCH] Bind to embedded interface method --- codegen/field.go | 249 +++++++++++++++++----------- codegen/testserver/embedded.go | 13 ++ codegen/testserver/embedded.graphql | 5 + codegen/testserver/embedded_test.go | 20 +++ codegen/testserver/generated.go | 139 ++++++++++++++++ codegen/testserver/resolver.go | 3 + codegen/testserver/stub.go | 4 + 7 files changed, 336 insertions(+), 97 deletions(-) diff --git a/codegen/field.go b/codegen/field.go index 738a55413b0..6cf0189e7be 100644 --- a/codegen/field.go +++ b/codegen/field.go @@ -184,107 +184,164 @@ func (b *builder) bindField(obj *Object, f *Field) (errret error) { } } -// findBindTarget attempts to match the name to a struct field or method +func (b *builder) findBindTarget(in types.Type, name string) (types.Object, error) { + switch t := in.(type) { + case *types.Named: + if _, ok := t.Underlying().(*types.Interface); ok { + return nil, errors.New("can't bind to an interface at root") + } + case *types.Interface: + return nil, errors.New("can't bind to an interface at root") + } + + return b.findBindTargetRecur(in, name) +} + +// findBindTargetRecur attempts to match the name to a field or method on a Type // with the following priorites: // 1. Any Fields with a struct tag (see config.StructTag). Errors if more than one match is found // 2. Any method or field with a matching name. Errors if more than one match is found // 3. Same logic again for embedded fields -func (b *builder) findBindTarget(named *types.Named, name string) (types.Object, error) { - strukt, isStruct := named.Underlying().(*types.Struct) - if isStruct { - // NOTE: a struct tag will override both methods and fields - // Bind to struct tag - found, err := b.findBindStructTagTarget(strukt, name) - if found != nil || err != nil { - return found, err - } +func (b *builder) findBindTargetRecur(t types.Type, name string) (types.Object, error) { + // NOTE: a struct tag will override both methods and fields + // Bind to struct tag + found, err := b.findBindStructTagTarget(t, name) + if found != nil || err != nil { + return found, err } // Search for a method to bind to - var foundMethod types.Object - for i := 0; i < named.NumMethods(); i++ { - method := named.Method(i) - if !method.Exported() || !strings.EqualFold(method.Name(), name) { - continue - } - - if foundMethod != nil { - return nil, errors.Errorf("found more than one matching method to bind for %s", name) - } - - foundMethod = method + foundMethod, err := b.findBindMethodTarget(t, name) + if err != nil { + return nil, err } // Search for a field to bind to - if isStruct { - foundField, err := b.findBindFieldTarget(strukt, name) - if err != nil { - return nil, err - } + foundField, err := b.findBindFieldTarget(t, name) + if err != nil { + return nil, err + } - switch { - case foundField == nil && foundMethod == nil: - // Search embeds - return b.findBindEmbedsTarget(strukt, name) - case foundField == nil && foundMethod != nil: - // Bind to method - return foundMethod, nil - case foundField != nil && foundMethod == nil: - // Bind to field - return foundField, nil - case foundField != nil && foundMethod != nil: - // Error - return nil, errors.Errorf("found more than one way to bind for %s", name) - } + switch { + case foundField == nil && foundMethod != nil: + // Bind to method + return foundMethod, nil + case foundField != nil && foundMethod == nil: + // Bind to field + return foundField, nil + case foundField != nil && foundMethod != nil: + // Error + return nil, errors.Errorf("found more than one way to bind for %s", name) } - // Bind to method or don't bind at all - return foundMethod, nil + // Search embeds + return b.findBindEmbedsTarget(t, name) } -func (b *builder) findBindStructTagTarget(strukt *types.Struct, name string) (types.Object, error) { +func (b *builder) findBindStructTagTarget(in types.Type, name string) (types.Object, error) { if b.Config.StructTag == "" { return nil, nil } - var found types.Object - for i := 0; i < strukt.NumFields(); i++ { - field := strukt.Field(i) - if !field.Exported() || field.Embedded() { - continue - } - tags := reflect.StructTag(strukt.Tag(i)) - if val, ok := tags.Lookup(b.Config.StructTag); ok && equalFieldName(val, name) { - if found != nil { - return nil, errors.Errorf("tag %s is ambigious; multiple fields have the same tag value of %s", b.Config.StructTag, val) + switch t := in.(type) { + case *types.Named: + return b.findBindStructTagTarget(t.Underlying(), name) + case *types.Struct: + var found types.Object + for i := 0; i < t.NumFields(); i++ { + field := t.Field(i) + if !field.Exported() || field.Embedded() { + continue } + tags := reflect.StructTag(t.Tag(i)) + if val, ok := tags.Lookup(b.Config.StructTag); ok && equalFieldName(val, name) { + if found != nil { + return nil, errors.Errorf("tag %s is ambigious; multiple fields have the same tag value of %s", b.Config.StructTag, val) + } - found = field + found = field + } } + + return found, nil } - return found, nil + return nil, nil +} + +func (b *builder) findBindMethodTarget(in types.Type, name string) (types.Object, error) { + switch t := in.(type) { + case *types.Named: + if _, ok := t.Underlying().(*types.Interface); ok { + return b.findBindMethodTarget(t.Underlying(), name) + } + + return b.findBindMethoderTarget(t.Method, t.NumMethods(), name) + case *types.Interface: + // FIX-ME: Should use ExplicitMethod here? What's the difference? + return b.findBindMethoderTarget(t.Method, t.NumMethods(), name) + } + + return nil, nil } -func (b *builder) findBindFieldTarget(strukt *types.Struct, name string) (types.Object, error) { +func (b *builder) findBindMethoderTarget(methodFunc func(i int) *types.Func, methodCount int, name string) (types.Object, error) { var found types.Object - for i := 0; i < strukt.NumFields(); i++ { - field := strukt.Field(i) - if !field.Exported() || !equalFieldName(field.Name(), name) { + for i := 0; i < methodCount; i++ { + method := methodFunc(i) + if !method.Exported() || !strings.EqualFold(method.Name(), name) { continue } if found != nil { - return nil, errors.Errorf("found more than one matching field to bind for %s", name) + return nil, errors.Errorf("found more than one matching method to bind for %s", name) } - found = field + found = method } return found, nil } -func (b *builder) findBindEmbedsTarget(strukt *types.Struct, name string) (types.Object, error) { +func (b *builder) findBindFieldTarget(in types.Type, name string) (types.Object, error) { + switch t := in.(type) { + case *types.Named: + return b.findBindFieldTarget(t.Underlying(), name) + case *types.Struct: + var found types.Object + for i := 0; i < t.NumFields(); i++ { + field := t.Field(i) + if !field.Exported() || !equalFieldName(field.Name(), name) { + continue + } + + if found != nil { + return nil, errors.Errorf("found more than one matching field to bind for %s", name) + } + + found = field + } + + return found, nil + } + + return nil, nil +} + +func (b *builder) findBindEmbedsTarget(in types.Type, name string) (types.Object, error) { + switch t := in.(type) { + case *types.Named: + return b.findBindEmbedsTarget(t.Underlying(), name) + case *types.Struct: + return b.findBindStructEmbedsTarget(t, name) + case *types.Interface: + return b.findBindInterfaceEmbedsTarget(t, name) + } + + return nil, nil +} + +func (b *builder) findBindStructEmbedsTarget(strukt *types.Struct, name string) (types.Object, error) { var found types.Object for i := 0; i < strukt.NumFields(); i++ { field := strukt.Field(i) @@ -297,41 +354,39 @@ func (b *builder) findBindEmbedsTarget(strukt *types.Struct, name string) (types fieldType = ptr.Elem() } - switch fieldType := fieldType.(type) { - case *types.Named: - f, err := b.findBindTarget(fieldType, name) - if err != nil { - return nil, err - } - if f != nil && found != nil { - return nil, errors.Errorf("found more than one way to bind for %s", name) - } - if f != nil { - found = f - } - case *types.Struct: - f, err := b.findBindStructTagTarget(fieldType, name) - if err != nil { - return nil, err - } - if f != nil && found != nil { - return nil, errors.Errorf("found more than one way to bind for %s", name) - } - if f != nil { - found = f - continue - } + f, err := b.findBindTargetRecur(fieldType, name) + if err != nil { + return nil, err + } - f, err = b.findBindFieldTarget(fieldType, name) - if err != nil { - return nil, err - } - if f != nil && found != nil { - return nil, errors.Errorf("found more than one way to bind for %s", name) - } - if f != nil { - found = f - } + if f != nil && found != nil { + return nil, errors.Errorf("found more than one way to bind for %s", name) + } + + if f != nil { + found = f + } + } + + return found, nil +} + +func (b *builder) findBindInterfaceEmbedsTarget(iface *types.Interface, name string) (types.Object, error) { + var found types.Object + for i := 0; i < iface.NumEmbeddeds(); i++ { + embeddedType := iface.EmbeddedType(i) + + f, err := b.findBindTargetRecur(embeddedType, name) + if err != nil { + return nil, err + } + + if f != nil && found != nil { + return nil, errors.Errorf("found more than one way to bind for %s", name) + } + + if f != nil { + found = f } } diff --git a/codegen/testserver/embedded.go b/codegen/testserver/embedded.go index 83d56962e93..2052281d884 100644 --- a/codegen/testserver/embedded.go +++ b/codegen/testserver/embedded.go @@ -28,3 +28,16 @@ type unexportedEmbeddedPointer struct{} func (*unexportedEmbeddedPointer) UnexportedEmbeddedPointerExportedMethod() string { return "UnexportedEmbeddedPointerExportedMethodResponse" } + +// EmbeddedCase3 model +type EmbeddedCase3 struct { + unexportedEmbeddedInterface +} + +type unexportedEmbeddedInterface interface { + nestedInterface +} + +type nestedInterface interface { + UnexportedEmbeddedInterfaceExportedMethod() string +} diff --git a/codegen/testserver/embedded.graphql b/codegen/testserver/embedded.graphql index ea83c955576..99cbd61c7c3 100644 --- a/codegen/testserver/embedded.graphql +++ b/codegen/testserver/embedded.graphql @@ -1,6 +1,7 @@ extend type Query { embeddedCase1: EmbeddedCase1 embeddedCase2: EmbeddedCase2 + embeddedCase3: EmbeddedCase3 } type EmbeddedCase1 @goModel(model:"testserver.EmbeddedCase1") { @@ -10,3 +11,7 @@ type EmbeddedCase1 @goModel(model:"testserver.EmbeddedCase1") { type EmbeddedCase2 @goModel(model:"testserver.EmbeddedCase2") { unexportedEmbeddedPointerExportedMethod: String! } + +type EmbeddedCase3 @goModel(model:"testserver.EmbeddedCase3") { + unexportedEmbeddedInterfaceExportedMethod: String! +} diff --git a/codegen/testserver/embedded_test.go b/codegen/testserver/embedded_test.go index ab6d4bdd863..4075ab17c96 100644 --- a/codegen/testserver/embedded_test.go +++ b/codegen/testserver/embedded_test.go @@ -9,6 +9,12 @@ import ( "github.com/stretchr/testify/require" ) +type fakeUnexportedEmbeddedInterface struct{} + +func (*fakeUnexportedEmbeddedInterface) UnexportedEmbeddedInterfaceExportedMethod() string { + return "UnexportedEmbeddedInterfaceExportedMethod" +} + func TestEmbedded(t *testing.T) { resolver := &Stub{} resolver.QueryResolver.EmbeddedCase1 = func(ctx context.Context) (*EmbeddedCase1, error) { @@ -17,6 +23,9 @@ func TestEmbedded(t *testing.T) { resolver.QueryResolver.EmbeddedCase2 = func(ctx context.Context) (*EmbeddedCase2, error) { return &EmbeddedCase2{&unexportedEmbeddedPointer{}}, nil } + resolver.QueryResolver.EmbeddedCase3 = func(ctx context.Context) (*EmbeddedCase3, error) { + return &EmbeddedCase3{&fakeUnexportedEmbeddedInterface{}}, nil + } c := client.New(handler.GraphQL( NewExecutableSchema(Config{Resolvers: resolver}), @@ -43,4 +52,15 @@ func TestEmbedded(t *testing.T) { require.NoError(t, err) require.Equal(t, resp.EmbeddedCase2.UnexportedEmbeddedPointerExportedMethod, "UnexportedEmbeddedPointerExportedMethodResponse") }) + + t.Run("embedded case 3", func(t *testing.T) { + var resp struct { + EmbeddedCase3 struct { + UnexportedEmbeddedInterfaceExportedMethod string + } + } + err := c.Post(`query { embeddedCase3 { unexportedEmbeddedInterfaceExportedMethod } }`, &resp) + require.NoError(t, err) + require.Equal(t, resp.EmbeddedCase3.UnexportedEmbeddedInterfaceExportedMethod, "UnexportedEmbeddedInterfaceExportedMethod") + }) } diff --git a/codegen/testserver/generated.go b/codegen/testserver/generated.go index 70c762446c1..bc76888bcb7 100644 --- a/codegen/testserver/generated.go +++ b/codegen/testserver/generated.go @@ -117,6 +117,10 @@ type ComplexityRoot struct { UnexportedEmbeddedPointerExportedMethod func(childComplexity int) int } + EmbeddedCase3 struct { + UnexportedEmbeddedInterfaceExportedMethod func(childComplexity int) int + } + EmbeddedDefaultScalar struct { Value func(childComplexity int) int } @@ -234,6 +238,7 @@ type ComplexityRoot struct { DirectiveUnimplemented func(childComplexity int) int EmbeddedCase1 func(childComplexity int) int EmbeddedCase2 func(childComplexity int) int + EmbeddedCase3 func(childComplexity int) int ErrorBubble func(childComplexity int) int Errors func(childComplexity int) int Fallback func(childComplexity int, arg FallbackToStringEncoding) int @@ -378,6 +383,7 @@ type QueryResolver interface { DirectiveUnimplemented(ctx context.Context) (*string, error) EmbeddedCase1(ctx context.Context) (*EmbeddedCase1, error) EmbeddedCase2(ctx context.Context) (*EmbeddedCase2, error) + EmbeddedCase3(ctx context.Context) (*EmbeddedCase3, error) Shapes(ctx context.Context) ([]Shape, error) NoShape(ctx context.Context) (Shape, error) MapStringInterface(ctx context.Context, in map[string]interface{}) (map[string]interface{}, error) @@ -529,6 +535,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.EmbeddedCase2.UnexportedEmbeddedPointerExportedMethod(childComplexity), true + case "EmbeddedCase3.unexportedEmbeddedInterfaceExportedMethod": + if e.complexity.EmbeddedCase3.UnexportedEmbeddedInterfaceExportedMethod == nil { + break + } + + return e.complexity.EmbeddedCase3.UnexportedEmbeddedInterfaceExportedMethod(childComplexity), true + case "EmbeddedDefaultScalar.value": if e.complexity.EmbeddedDefaultScalar.Value == nil { break @@ -966,6 +979,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Query.EmbeddedCase2(childComplexity), true + case "Query.embeddedCase3": + if e.complexity.Query.EmbeddedCase3 == nil { + break + } + + return e.complexity.Query.EmbeddedCase3(childComplexity), true + case "Query.errorBubble": if e.complexity.Query.ErrorBubble == nil { break @@ -1562,6 +1582,7 @@ type ObjectDirectivesWithCustomGoModel { &ast.Source{Name: "embedded.graphql", Input: `extend type Query { embeddedCase1: EmbeddedCase1 embeddedCase2: EmbeddedCase2 + embeddedCase3: EmbeddedCase3 } type EmbeddedCase1 @goModel(model:"testserver.EmbeddedCase1") { @@ -1571,6 +1592,10 @@ type EmbeddedCase1 @goModel(model:"testserver.EmbeddedCase1") { type EmbeddedCase2 @goModel(model:"testserver.EmbeddedCase2") { unexportedEmbeddedPointerExportedMethod: String! } + +type EmbeddedCase3 @goModel(model:"testserver.EmbeddedCase3") { + unexportedEmbeddedInterfaceExportedMethod: String! +} `}, &ast.Source{Name: "interfaces.graphql", Input: `extend type Query { shapes: [Shape] @@ -3275,6 +3300,40 @@ func (ec *executionContext) _EmbeddedCase2_unexportedEmbeddedPointerExportedMeth return ec.marshalNString2string(ctx, field.Selections, res) } +func (ec *executionContext) _EmbeddedCase3_unexportedEmbeddedInterfaceExportedMethod(ctx context.Context, field graphql.CollectedField, obj *EmbeddedCase3) (ret graphql.Marshaler) { + ctx = ec.Tracer.StartFieldExecution(ctx, field) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + ec.Tracer.EndFieldExecution(ctx) + }() + rctx := &graphql.ResolverContext{ + Object: "EmbeddedCase3", + Field: field, + Args: nil, + IsMethod: true, + } + ctx = graphql.WithResolverContext(ctx, rctx) + ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) + resTmp := ec._fieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.UnexportedEmbeddedInterfaceExportedMethod(), nil + }) + + if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + rctx.Result = res + ctx = ec.Tracer.StartFieldChildExecution(ctx) + return ec.marshalNString2string(ctx, field.Selections, res) +} + func (ec *executionContext) _EmbeddedDefaultScalar_value(ctx context.Context, field graphql.CollectedField, obj *EmbeddedDefaultScalar) (ret graphql.Marshaler) { ctx = ec.Tracer.StartFieldExecution(ctx, field) defer func() { @@ -5756,6 +5815,37 @@ func (ec *executionContext) _Query_embeddedCase2(ctx context.Context, field grap return ec.marshalOEmbeddedCase22ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋcodegenᚋtestserverᚐEmbeddedCase2(ctx, field.Selections, res) } +func (ec *executionContext) _Query_embeddedCase3(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + ctx = ec.Tracer.StartFieldExecution(ctx, field) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + ec.Tracer.EndFieldExecution(ctx) + }() + rctx := &graphql.ResolverContext{ + Object: "Query", + Field: field, + Args: nil, + IsMethod: true, + } + ctx = graphql.WithResolverContext(ctx, rctx) + ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) + resTmp := ec._fieldMiddleware(ctx, nil, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().EmbeddedCase3(rctx) + }) + + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*EmbeddedCase3) + rctx.Result = res + ctx = ec.Tracer.StartFieldChildExecution(ctx) + return ec.marshalOEmbeddedCase32ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋcodegenᚋtestserverᚐEmbeddedCase3(ctx, field.Selections, res) +} + func (ec *executionContext) _Query_shapes(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { ctx = ec.Tracer.StartFieldExecution(ctx, field) defer func() { @@ -9229,6 +9319,33 @@ func (ec *executionContext) _EmbeddedCase2(ctx context.Context, sel ast.Selectio return out } +var embeddedCase3Implementors = []string{"EmbeddedCase3"} + +func (ec *executionContext) _EmbeddedCase3(ctx context.Context, sel ast.SelectionSet, obj *EmbeddedCase3) graphql.Marshaler { + fields := graphql.CollectFields(ec.RequestContext, sel, embeddedCase3Implementors) + + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("EmbeddedCase3") + case "unexportedEmbeddedInterfaceExportedMethod": + out.Values[i] = ec._EmbeddedCase3_unexportedEmbeddedInterfaceExportedMethod(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalids > 0 { + return graphql.Null + } + return out +} + var embeddedDefaultScalarImplementors = []string{"EmbeddedDefaultScalar"} func (ec *executionContext) _EmbeddedDefaultScalar(ctx context.Context, sel ast.SelectionSet, obj *EmbeddedDefaultScalar) graphql.Marshaler { @@ -10309,6 +10426,17 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr res = ec._Query_embeddedCase2(ctx, field) return res }) + case "embeddedCase3": + field := field + out.Concurrently(i, func() (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_embeddedCase3(ctx, field) + return res + }) case "shapes": field := field out.Concurrently(i, func() (res graphql.Marshaler) { @@ -11976,6 +12104,17 @@ func (ec *executionContext) marshalOEmbeddedCase22ᚖgithubᚗcomᚋ99designsᚋ return ec._EmbeddedCase2(ctx, sel, v) } +func (ec *executionContext) marshalOEmbeddedCase32githubᚗcomᚋ99designsᚋgqlgenᚋcodegenᚋtestserverᚐEmbeddedCase3(ctx context.Context, sel ast.SelectionSet, v EmbeddedCase3) graphql.Marshaler { + return ec._EmbeddedCase3(ctx, sel, &v) +} + +func (ec *executionContext) marshalOEmbeddedCase32ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋcodegenᚋtestserverᚐEmbeddedCase3(ctx context.Context, sel ast.SelectionSet, v *EmbeddedCase3) graphql.Marshaler { + if v == nil { + return graphql.Null + } + return ec._EmbeddedCase3(ctx, sel, v) +} + func (ec *executionContext) marshalOError2githubᚗcomᚋ99designsᚋgqlgenᚋcodegenᚋtestserverᚐError(ctx context.Context, sel ast.SelectionSet, v Error) graphql.Marshaler { return ec._Error(ctx, sel, &v) } diff --git a/codegen/testserver/resolver.go b/codegen/testserver/resolver.go index a4edc9f6afc..3572a3498eb 100644 --- a/codegen/testserver/resolver.go +++ b/codegen/testserver/resolver.go @@ -185,6 +185,9 @@ func (r *queryResolver) EmbeddedCase1(ctx context.Context) (*EmbeddedCase1, erro func (r *queryResolver) EmbeddedCase2(ctx context.Context) (*EmbeddedCase2, error) { panic("not implemented") } +func (r *queryResolver) EmbeddedCase3(ctx context.Context) (*EmbeddedCase3, error) { + panic("not implemented") +} func (r *queryResolver) Shapes(ctx context.Context) ([]Shape, error) { panic("not implemented") } diff --git a/codegen/testserver/stub.go b/codegen/testserver/stub.go index 0d4d3ee26c7..e33a87e10de 100644 --- a/codegen/testserver/stub.go +++ b/codegen/testserver/stub.go @@ -65,6 +65,7 @@ type Stub struct { DirectiveUnimplemented func(ctx context.Context) (*string, error) EmbeddedCase1 func(ctx context.Context) (*EmbeddedCase1, error) EmbeddedCase2 func(ctx context.Context) (*EmbeddedCase2, error) + EmbeddedCase3 func(ctx context.Context) (*EmbeddedCase3, error) Shapes func(ctx context.Context) ([]Shape, error) NoShape func(ctx context.Context) (Shape, error) MapStringInterface func(ctx context.Context, in map[string]interface{}) (map[string]interface{}, error) @@ -271,6 +272,9 @@ func (r *stubQuery) EmbeddedCase1(ctx context.Context) (*EmbeddedCase1, error) { func (r *stubQuery) EmbeddedCase2(ctx context.Context) (*EmbeddedCase2, error) { return r.QueryResolver.EmbeddedCase2(ctx) } +func (r *stubQuery) EmbeddedCase3(ctx context.Context) (*EmbeddedCase3, error) { + return r.QueryResolver.EmbeddedCase3(ctx) +} func (r *stubQuery) Shapes(ctx context.Context) ([]Shape, error) { return r.QueryResolver.Shapes(ctx) }