From d29d098fdf508b2e36cbc1c8c1415b2ca995ff8a Mon Sep 17 00:00:00 2001 From: Kevin Fang Date: Fri, 16 Sep 2022 20:09:16 -0400 Subject: [PATCH] fix field merging behavior for fragments on interfaces (#2380) Co-authored-by: Kevin Fang --- .../followschema/interfaces.generated.go | 242 ++++++++++++ .../followschema/interfaces.graphql | 9 + .../followschema/interfaces_test.go | 41 ++ codegen/testserver/followschema/models-gen.go | 10 + codegen/testserver/followschema/resolver.go | 5 + .../followschema/root_.generated.go | 43 +++ .../followschema/schema.generated.go | 67 ++++ codegen/testserver/followschema/stub.go | 4 + codegen/testserver/singlefile/generated.go | 352 ++++++++++++++++++ .../testserver/singlefile/interfaces.graphql | 9 + .../testserver/singlefile/interfaces_test.go | 41 ++ codegen/testserver/singlefile/models-gen.go | 10 + codegen/testserver/singlefile/resolver.go | 5 + codegen/testserver/singlefile/stub.go | 4 + graphql/executable_schema.go | 5 + 15 files changed, 847 insertions(+) diff --git a/codegen/testserver/followschema/interfaces.generated.go b/codegen/testserver/followschema/interfaces.generated.go index 5e89ac7ff41..15d1fda2ecd 100644 --- a/codegen/testserver/followschema/interfaces.generated.go +++ b/codegen/testserver/followschema/interfaces.generated.go @@ -196,6 +196,53 @@ func (ec *executionContext) fieldContext_Cat_species(ctx context.Context, field return fc, nil } +func (ec *executionContext) _Cat_size(ctx context.Context, field graphql.CollectedField, obj *Cat) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Cat_size(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp := ec._fieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Size, nil + }) + + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*Size) + fc.Result = res + return ec.marshalNSize2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋcodegenᚋtestserverᚋfollowschemaᚐSize(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Cat_size(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Cat", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "height": + return ec.fieldContext_Size_height(ctx, field) + case "weight": + return ec.fieldContext_Size_weight(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Size", field.Name) + }, + } + return fc, nil +} + func (ec *executionContext) _Cat_catBreed(ctx context.Context, field graphql.CollectedField, obj *Cat) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Cat_catBreed(ctx, field) if err != nil { @@ -685,6 +732,53 @@ func (ec *executionContext) fieldContext_Dog_species(ctx context.Context, field return fc, nil } +func (ec *executionContext) _Dog_size(ctx context.Context, field graphql.CollectedField, obj *Dog) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Dog_size(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp := ec._fieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Size, nil + }) + + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*Size) + fc.Result = res + return ec.marshalNSize2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋcodegenᚋtestserverᚋfollowschemaᚐSize(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Dog_size(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Dog", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "height": + return ec.fieldContext_Size_height(ctx, field) + case "weight": + return ec.fieldContext_Size_weight(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Size", field.Name) + }, + } + return fc, nil +} + func (ec *executionContext) _Dog_dogBreed(ctx context.Context, field graphql.CollectedField, obj *Dog) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Dog_dogBreed(ctx, field) if err != nil { @@ -884,6 +978,88 @@ func (ec *executionContext) fieldContext_Rectangle_coordinates(ctx context.Conte return fc, nil } +func (ec *executionContext) _Size_height(ctx context.Context, field graphql.CollectedField, obj *Size) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Size_height(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp := ec._fieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Height, nil + }) + + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(int) + fc.Result = res + return ec.marshalNInt2int(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Size_height(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Size", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Int does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Size_weight(ctx context.Context, field graphql.CollectedField, obj *Size) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Size_weight(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp := ec._fieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Weight, nil + }) + + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(int) + fc.Result = res + return ec.marshalNInt2int(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Size_weight(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Size", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Int does not have child fields") + }, + } + return fc, nil +} + // endregion **************************** field.gotpl ***************************** // region **************************** input.gotpl ***************************** @@ -1045,6 +1221,13 @@ func (ec *executionContext) _Cat(ctx context.Context, sel ast.SelectionSet, obj out.Values[i] = ec._Cat_species(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } + case "size": + + out.Values[i] = ec._Cat_size(ctx, field, obj) + if out.Values[i] == graphql.Null { invalids++ } @@ -1225,6 +1408,13 @@ func (ec *executionContext) _Dog(ctx context.Context, sel ast.SelectionSet, obj out.Values[i] = ec._Dog_species(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } + case "size": + + out.Values[i] = ec._Dog_size(ctx, field, obj) + if out.Values[i] == graphql.Null { invalids++ } @@ -1283,6 +1473,41 @@ func (ec *executionContext) _Rectangle(ctx context.Context, sel ast.SelectionSet return out } +var sizeImplementors = []string{"Size"} + +func (ec *executionContext) _Size(ctx context.Context, sel ast.SelectionSet, obj *Size) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, sizeImplementors) + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("Size") + case "height": + + out.Values[i] = ec._Size_height(ctx, field, obj) + + if out.Values[i] == graphql.Null { + invalids++ + } + case "weight": + + out.Values[i] = ec._Size_weight(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 +} + // endregion **************************** object.gotpl **************************** // region ***************************** type.gotpl ***************************** @@ -1307,6 +1532,16 @@ func (ec *executionContext) marshalNShapeUnion2githubᚗcomᚋ99designsᚋgqlgen return ec._ShapeUnion(ctx, sel, v) } +func (ec *executionContext) marshalNSize2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋcodegenᚋtestserverᚋfollowschemaᚐSize(ctx context.Context, sel ast.SelectionSet, v *Size) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._Size(ctx, sel, v) +} + func (ec *executionContext) marshalOAnimal2githubᚗcomᚋ99designsᚋgqlgenᚋcodegenᚋtestserverᚋfollowschemaᚐAnimal(ctx context.Context, sel ast.SelectionSet, v Animal) graphql.Marshaler { if v == nil { return graphql.Null @@ -1332,6 +1567,13 @@ func (ec *executionContext) marshalOCoordinates2githubᚗcomᚋ99designsᚋgqlge return ec._Coordinates(ctx, sel, &v) } +func (ec *executionContext) marshalODog2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋcodegenᚋtestserverᚋfollowschemaᚐDog(ctx context.Context, sel ast.SelectionSet, v *Dog) graphql.Marshaler { + if v == nil { + return graphql.Null + } + return ec._Dog(ctx, sel, v) +} + func (ec *executionContext) marshalOShape2githubᚗcomᚋ99designsᚋgqlgenᚋcodegenᚋtestserverᚋfollowschemaᚐShape(ctx context.Context, sel ast.SelectionSet, v Shape) graphql.Marshaler { if v == nil { return graphql.Null diff --git a/codegen/testserver/followschema/interfaces.graphql b/codegen/testserver/followschema/interfaces.graphql index af5c17170c3..96c1daf0ea0 100644 --- a/codegen/testserver/followschema/interfaces.graphql +++ b/codegen/testserver/followschema/interfaces.graphql @@ -5,10 +5,17 @@ extend type Query { noShapeTypedNil: Shape @makeTypedNil animal: Animal @makeTypedNil notAnInterface: BackedByInterface + dog: Dog } interface Animal { species: String! + size: Size! +} + +type Size { + height: Int! + weight: Int! } type BackedByInterface { @@ -19,11 +26,13 @@ type BackedByInterface { type Dog implements Animal { species: String! + size: Size! dogBreed: String! } type Cat implements Animal { species: String! + size: Size! catBreed: String! } diff --git a/codegen/testserver/followschema/interfaces_test.go b/codegen/testserver/followschema/interfaces_test.go index d67c7da59f4..3a7820fe52c 100644 --- a/codegen/testserver/followschema/interfaces_test.go +++ b/codegen/testserver/followschema/interfaces_test.go @@ -253,4 +253,45 @@ func TestInterfaces(t *testing.T) { require.Equal(t, float64(1), resp.Shapes[1].Coordinates.X) require.Equal(t, float64(1), resp.Shapes[1].Coordinates.Y) }) + + t.Run("fragment on interface must return merged fields", func(t *testing.T) { + resolvers := &Stub{} + resolvers.QueryResolver.Dog = func(ctx context.Context) (dog *Dog, err error) { + return &Dog{ + Size: &Size{ + Height: 100, + Weight: 35, + }, + }, nil + } + + c := client.New(handler.NewDefaultServer(NewExecutableSchema(Config{Resolvers: resolvers}))) + var resp struct { + Dog struct { + Size struct { + Height int + Weight int + } + } + } + + c.MustPost(` + { + dog { + size { + height + } + ...AnimalWeight + } + } + fragment AnimalWeight on Animal { + size { + weight + } + } + `, &resp) + + require.Equal(t, 100, resp.Dog.Size.Height) + require.Equal(t, 35, resp.Dog.Size.Weight) + }) } diff --git a/codegen/testserver/followschema/models-gen.go b/codegen/testserver/followschema/models-gen.go index 22b8a19caac..0cc3b4a24e6 100644 --- a/codegen/testserver/followschema/models-gen.go +++ b/codegen/testserver/followschema/models-gen.go @@ -12,6 +12,7 @@ import ( type Animal interface { IsAnimal() GetSpecies() string + GetSize() *Size } type ContentChild interface { @@ -44,11 +45,13 @@ func (B) IsTestUnion() {} type Cat struct { Species string `json:"species"` + Size *Size `json:"size"` CatBreed string `json:"catBreed"` } func (Cat) IsAnimal() {} func (this Cat) GetSpecies() string { return this.Species } +func (this Cat) GetSize() *Size { return this.Size } type CheckIssue896 struct { ID *int `json:"id"` @@ -83,11 +86,13 @@ type DefaultParametersMirror struct { type Dog struct { Species string `json:"species"` + Size *Size `json:"size"` DogBreed string `json:"dogBreed"` } func (Dog) IsAnimal() {} func (this Dog) GetSpecies() string { return this.Species } +func (this Dog) GetSize() *Size { return this.Size } type EmbeddedDefaultScalar struct { Value *string `json:"value"` @@ -162,6 +167,11 @@ type Pet struct { Friends []*Pet `json:"friends"` } +type Size struct { + Height int `json:"height"` + Weight int `json:"weight"` +} + type Slices struct { Test1 []*string `json:"test1"` Test2 []string `json:"test2"` diff --git a/codegen/testserver/followschema/resolver.go b/codegen/testserver/followschema/resolver.go index 409e7beb040..77b6eda405b 100644 --- a/codegen/testserver/followschema/resolver.go +++ b/codegen/testserver/followschema/resolver.go @@ -292,6 +292,11 @@ func (r *queryResolver) NotAnInterface(ctx context.Context) (BackedByInterface, panic("not implemented") } +// // foo +func (r *queryResolver) Dog(ctx context.Context) (*Dog, error) { + panic("not implemented") +} + // // foo func (r *queryResolver) Issue896a(ctx context.Context) ([]*CheckIssue896, error) { panic("not implemented") diff --git a/codegen/testserver/followschema/root_.generated.go b/codegen/testserver/followschema/root_.generated.go index 9f7231b9e21..ec9a900cef3 100644 --- a/codegen/testserver/followschema/root_.generated.go +++ b/codegen/testserver/followschema/root_.generated.go @@ -98,6 +98,7 @@ type ComplexityRoot struct { Cat struct { CatBreed func(childComplexity int) int + Size func(childComplexity int) int Species func(childComplexity int) int } @@ -142,6 +143,7 @@ type ComplexityRoot struct { Dog struct { DogBreed func(childComplexity int) int + Size func(childComplexity int) int Species func(childComplexity int) int } @@ -306,6 +308,7 @@ type ComplexityRoot struct { DirectiveObject func(childComplexity int) int DirectiveObjectWithCustomGoModel func(childComplexity int) int DirectiveUnimplemented func(childComplexity int) int + Dog func(childComplexity int) int EmbeddedCase1 func(childComplexity int) int EmbeddedCase2 func(childComplexity int) int EmbeddedCase3 func(childComplexity int) int @@ -363,6 +366,11 @@ type ComplexityRoot struct { Width func(childComplexity int) int } + Size struct { + Height func(childComplexity int) int + Weight func(childComplexity int) int + } + Slices struct { Test1 func(childComplexity int) int Test2 func(childComplexity int) int @@ -544,6 +552,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Cat.CatBreed(childComplexity), true + case "Cat.size": + if e.complexity.Cat.Size == nil { + break + } + + return e.complexity.Cat.Size(childComplexity), true + case "Cat.species": if e.complexity.Cat.Species == nil { break @@ -663,6 +678,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Dog.DogBreed(childComplexity), true + case "Dog.size": + if e.complexity.Dog.Size == nil { + break + } + + return e.complexity.Dog.Size(childComplexity), true + case "Dog.species": if e.complexity.Dog.Species == nil { break @@ -1256,6 +1278,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Query.DirectiveUnimplemented(childComplexity), true + case "Query.dog": + if e.complexity.Query.Dog == nil { + break + } + + return e.complexity.Query.Dog(childComplexity), true + case "Query.embeddedCase1": if e.complexity.Query.EmbeddedCase1 == nil { break @@ -1675,6 +1704,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Rectangle.Width(childComplexity), true + case "Size.height": + if e.complexity.Size.Height == nil { + break + } + + return e.complexity.Size.Height(childComplexity), true + + case "Size.weight": + if e.complexity.Size.Weight == nil { + break + } + + return e.complexity.Size.Weight(childComplexity), true + case "Slices.test1": if e.complexity.Slices.Test1 == nil { break diff --git a/codegen/testserver/followschema/schema.generated.go b/codegen/testserver/followschema/schema.generated.go index d3a45046625..717b8c253d2 100644 --- a/codegen/testserver/followschema/schema.generated.go +++ b/codegen/testserver/followschema/schema.generated.go @@ -69,6 +69,7 @@ type QueryResolver interface { NoShapeTypedNil(ctx context.Context) (Shape, error) Animal(ctx context.Context) (Animal, error) NotAnInterface(ctx context.Context) (BackedByInterface, error) + Dog(ctx context.Context) (*Dog, error) Issue896a(ctx context.Context) ([]*CheckIssue896, error) MapStringInterface(ctx context.Context, in map[string]interface{}) (map[string]interface{}, error) MapNestedStringInterface(ctx context.Context, in *NestedMapInput) (map[string]interface{}, error) @@ -3296,6 +3297,52 @@ func (ec *executionContext) fieldContext_Query_notAnInterface(ctx context.Contex return fc, nil } +func (ec *executionContext) _Query_dog(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_dog(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp := ec._fieldMiddleware(ctx, nil, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().Dog(rctx) + }) + + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*Dog) + fc.Result = res + return ec.marshalODog2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋcodegenᚋtestserverᚋfollowschemaᚐDog(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_dog(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "species": + return ec.fieldContext_Dog_species(ctx, field) + case "size": + return ec.fieldContext_Dog_size(ctx, field) + case "dogBreed": + return ec.fieldContext_Dog_dogBreed(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Dog", field.Name) + }, + } + return fc, nil +} + func (ec *executionContext) _Query_issue896a(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Query_issue896a(ctx, field) if err != nil { @@ -6661,6 +6708,26 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) } + out.Concurrently(i, func() graphql.Marshaler { + return rrm(innerCtx) + }) + case "dog": + field := field + + innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_dog(ctx, field) + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + } + out.Concurrently(i, func() graphql.Marshaler { return rrm(innerCtx) }) diff --git a/codegen/testserver/followschema/stub.go b/codegen/testserver/followschema/stub.go index 95305fc12d3..0c554c303f3 100644 --- a/codegen/testserver/followschema/stub.go +++ b/codegen/testserver/followschema/stub.go @@ -88,6 +88,7 @@ type Stub struct { NoShapeTypedNil func(ctx context.Context) (Shape, error) Animal func(ctx context.Context) (Animal, error) NotAnInterface func(ctx context.Context) (BackedByInterface, error) + Dog func(ctx context.Context) (*Dog, error) Issue896a func(ctx context.Context) ([]*CheckIssue896, error) MapStringInterface func(ctx context.Context, in map[string]interface{}) (map[string]interface{}, error) MapNestedStringInterface func(ctx context.Context, in *NestedMapInput) (map[string]interface{}, error) @@ -393,6 +394,9 @@ func (r *stubQuery) Animal(ctx context.Context) (Animal, error) { func (r *stubQuery) NotAnInterface(ctx context.Context) (BackedByInterface, error) { return r.QueryResolver.NotAnInterface(ctx) } +func (r *stubQuery) Dog(ctx context.Context) (*Dog, error) { + return r.QueryResolver.Dog(ctx) +} func (r *stubQuery) Issue896a(ctx context.Context) ([]*CheckIssue896, error) { return r.QueryResolver.Issue896a(ctx) } diff --git a/codegen/testserver/singlefile/generated.go b/codegen/testserver/singlefile/generated.go index 320af50624a..3b220006e53 100644 --- a/codegen/testserver/singlefile/generated.go +++ b/codegen/testserver/singlefile/generated.go @@ -108,6 +108,7 @@ type ComplexityRoot struct { Cat struct { CatBreed func(childComplexity int) int + Size func(childComplexity int) int Species func(childComplexity int) int } @@ -152,6 +153,7 @@ type ComplexityRoot struct { Dog struct { DogBreed func(childComplexity int) int + Size func(childComplexity int) int Species func(childComplexity int) int } @@ -316,6 +318,7 @@ type ComplexityRoot struct { DirectiveObject func(childComplexity int) int DirectiveObjectWithCustomGoModel func(childComplexity int) int DirectiveUnimplemented func(childComplexity int) int + Dog func(childComplexity int) int EmbeddedCase1 func(childComplexity int) int EmbeddedCase2 func(childComplexity int) int EmbeddedCase3 func(childComplexity int) int @@ -373,6 +376,11 @@ type ComplexityRoot struct { Width func(childComplexity int) int } + Size struct { + Height func(childComplexity int) int + Weight func(childComplexity int) int + } + Slices struct { Test1 func(childComplexity int) int Test2 func(childComplexity int) int @@ -527,6 +535,7 @@ type QueryResolver interface { NoShapeTypedNil(ctx context.Context) (Shape, error) Animal(ctx context.Context) (Animal, error) NotAnInterface(ctx context.Context) (BackedByInterface, error) + Dog(ctx context.Context) (*Dog, error) Issue896a(ctx context.Context) ([]*CheckIssue896, error) MapStringInterface(ctx context.Context, in map[string]interface{}) (map[string]interface{}, error) MapNestedStringInterface(ctx context.Context, in *NestedMapInput) (map[string]interface{}, error) @@ -688,6 +697,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Cat.CatBreed(childComplexity), true + case "Cat.size": + if e.complexity.Cat.Size == nil { + break + } + + return e.complexity.Cat.Size(childComplexity), true + case "Cat.species": if e.complexity.Cat.Species == nil { break @@ -807,6 +823,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Dog.DogBreed(childComplexity), true + case "Dog.size": + if e.complexity.Dog.Size == nil { + break + } + + return e.complexity.Dog.Size(childComplexity), true + case "Dog.species": if e.complexity.Dog.Species == nil { break @@ -1400,6 +1423,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Query.DirectiveUnimplemented(childComplexity), true + case "Query.dog": + if e.complexity.Query.Dog == nil { + break + } + + return e.complexity.Query.Dog(childComplexity), true + case "Query.embeddedCase1": if e.complexity.Query.EmbeddedCase1 == nil { break @@ -1819,6 +1849,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Rectangle.Width(childComplexity), true + case "Size.height": + if e.complexity.Size.Height == nil { + break + } + + return e.complexity.Size.Height(childComplexity), true + + case "Size.weight": + if e.complexity.Size.Weight == nil { + break + } + + return e.complexity.Size.Weight(childComplexity), true + case "Slices.test1": if e.complexity.Slices.Test1 == nil { break @@ -3895,6 +3939,53 @@ func (ec *executionContext) fieldContext_Cat_species(ctx context.Context, field return fc, nil } +func (ec *executionContext) _Cat_size(ctx context.Context, field graphql.CollectedField, obj *Cat) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Cat_size(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp := ec._fieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Size, nil + }) + + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*Size) + fc.Result = res + return ec.marshalNSize2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋcodegenᚋtestserverᚋsinglefileᚐSize(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Cat_size(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Cat", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "height": + return ec.fieldContext_Size_height(ctx, field) + case "weight": + return ec.fieldContext_Size_weight(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Size", field.Name) + }, + } + return fc, nil +} + func (ec *executionContext) _Cat_catBreed(ctx context.Context, field graphql.CollectedField, obj *Cat) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Cat_catBreed(ctx, field) if err != nil { @@ -4574,6 +4665,53 @@ func (ec *executionContext) fieldContext_Dog_species(ctx context.Context, field return fc, nil } +func (ec *executionContext) _Dog_size(ctx context.Context, field graphql.CollectedField, obj *Dog) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Dog_size(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp := ec._fieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Size, nil + }) + + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*Size) + fc.Result = res + return ec.marshalNSize2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋcodegenᚋtestserverᚋsinglefileᚐSize(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Dog_size(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Dog", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "height": + return ec.fieldContext_Size_height(ctx, field) + case "weight": + return ec.fieldContext_Size_weight(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Size", field.Name) + }, + } + return fc, nil +} + func (ec *executionContext) _Dog_dogBreed(ctx context.Context, field graphql.CollectedField, obj *Dog) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Dog_dogBreed(ctx, field) if err != nil { @@ -9104,6 +9242,52 @@ func (ec *executionContext) fieldContext_Query_notAnInterface(ctx context.Contex return fc, nil } +func (ec *executionContext) _Query_dog(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_dog(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp := ec._fieldMiddleware(ctx, nil, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().Dog(rctx) + }) + + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*Dog) + fc.Result = res + return ec.marshalODog2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋcodegenᚋtestserverᚋsinglefileᚐDog(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_dog(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "species": + return ec.fieldContext_Dog_species(ctx, field) + case "size": + return ec.fieldContext_Dog_size(ctx, field) + case "dogBreed": + return ec.fieldContext_Dog_dogBreed(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Dog", field.Name) + }, + } + return fc, nil +} + func (ec *executionContext) _Query_issue896a(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Query_issue896a(ctx, field) if err != nil { @@ -10665,6 +10849,88 @@ func (ec *executionContext) fieldContext_Rectangle_coordinates(ctx context.Conte return fc, nil } +func (ec *executionContext) _Size_height(ctx context.Context, field graphql.CollectedField, obj *Size) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Size_height(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp := ec._fieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Height, nil + }) + + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(int) + fc.Result = res + return ec.marshalNInt2int(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Size_height(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Size", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Int does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Size_weight(ctx context.Context, field graphql.CollectedField, obj *Size) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Size_weight(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp := ec._fieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Weight, nil + }) + + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(int) + fc.Result = res + return ec.marshalNInt2int(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Size_weight(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Size", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Int does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) _Slices_test1(ctx context.Context, field graphql.CollectedField, obj *Slices) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Slices_test1(ctx, field) if err != nil { @@ -15081,6 +15347,13 @@ func (ec *executionContext) _Cat(ctx context.Context, sel ast.SelectionSet, obj out.Values[i] = ec._Cat_species(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } + case "size": + + out.Values[i] = ec._Cat_size(ctx, field, obj) + if out.Values[i] == graphql.Null { invalids++ } @@ -15365,6 +15638,13 @@ func (ec *executionContext) _Dog(ctx context.Context, sel ast.SelectionSet, obj out.Values[i] = ec._Dog_species(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } + case "size": + + out.Values[i] = ec._Dog_size(ctx, field, obj) + if out.Values[i] == graphql.Null { invalids++ } @@ -17367,6 +17647,26 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) } + out.Concurrently(i, func() graphql.Marshaler { + return rrm(innerCtx) + }) + case "dog": + field := field + + innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_dog(ctx, field) + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + } + out.Concurrently(i, func() graphql.Marshaler { return rrm(innerCtx) }) @@ -18032,6 +18332,41 @@ func (ec *executionContext) _Rectangle(ctx context.Context, sel ast.SelectionSet return out } +var sizeImplementors = []string{"Size"} + +func (ec *executionContext) _Size(ctx context.Context, sel ast.SelectionSet, obj *Size) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, sizeImplementors) + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("Size") + case "height": + + out.Values[i] = ec._Size_height(ctx, field, obj) + + if out.Values[i] == graphql.Null { + invalids++ + } + case "weight": + + out.Values[i] = ec._Size_weight(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 slicesImplementors = []string{"Slices"} func (ec *executionContext) _Slices(ctx context.Context, sel ast.SelectionSet, obj *Slices) graphql.Marshaler { @@ -19361,6 +19696,16 @@ func (ec *executionContext) marshalNShapeUnion2githubᚗcomᚋ99designsᚋgqlgen return ec._ShapeUnion(ctx, sel, v) } +func (ec *executionContext) marshalNSize2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋcodegenᚋtestserverᚋsinglefileᚐSize(ctx context.Context, sel ast.SelectionSet, v *Size) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._Size(ctx, sel, v) +} + func (ec *executionContext) unmarshalNSpecialInput2githubᚗcomᚋ99designsᚋgqlgenᚋcodegenᚋtestserverᚋsinglefileᚐSpecialInput(ctx context.Context, v interface{}) (SpecialInput, error) { res, err := ec.unmarshalInputSpecialInput(ctx, v) return res, graphql.ErrorOnPath(ctx, err) @@ -20073,6 +20418,13 @@ func (ec *executionContext) marshalODefaultScalarImplementation2ᚖstring(ctx co return res } +func (ec *executionContext) marshalODog2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋcodegenᚋtestserverᚋsinglefileᚐDog(ctx context.Context, sel ast.SelectionSet, v *Dog) graphql.Marshaler { + if v == nil { + return graphql.Null + } + return ec._Dog(ctx, sel, v) +} + func (ec *executionContext) marshalOEmbeddedCase12ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋcodegenᚋtestserverᚋsinglefileᚐEmbeddedCase1(ctx context.Context, sel ast.SelectionSet, v *EmbeddedCase1) graphql.Marshaler { if v == nil { return graphql.Null diff --git a/codegen/testserver/singlefile/interfaces.graphql b/codegen/testserver/singlefile/interfaces.graphql index 4b6480be83c..24320314444 100644 --- a/codegen/testserver/singlefile/interfaces.graphql +++ b/codegen/testserver/singlefile/interfaces.graphql @@ -5,10 +5,17 @@ extend type Query { noShapeTypedNil: Shape @makeTypedNil animal: Animal @makeTypedNil notAnInterface: BackedByInterface + dog: Dog } interface Animal { species: String! + size: Size! +} + +type Size { + height: Int! + weight: Int! } type BackedByInterface { @@ -19,11 +26,13 @@ type BackedByInterface { type Dog implements Animal { species: String! + size: Size! dogBreed: String! } type Cat implements Animal { species: String! + size: Size! catBreed: String! } diff --git a/codegen/testserver/singlefile/interfaces_test.go b/codegen/testserver/singlefile/interfaces_test.go index 6093a8d7024..23b3fb0206b 100644 --- a/codegen/testserver/singlefile/interfaces_test.go +++ b/codegen/testserver/singlefile/interfaces_test.go @@ -253,4 +253,45 @@ func TestInterfaces(t *testing.T) { require.Equal(t, float64(1), resp.Shapes[1].Coordinates.X) require.Equal(t, float64(1), resp.Shapes[1].Coordinates.Y) }) + + t.Run("fragment on interface must return merged fields", func(t *testing.T) { + resolvers := &Stub{} + resolvers.QueryResolver.Dog = func(ctx context.Context) (dog *Dog, err error) { + return &Dog{ + Size: &Size{ + Height: 100, + Weight: 35, + }, + }, nil + } + + c := client.New(handler.NewDefaultServer(NewExecutableSchema(Config{Resolvers: resolvers}))) + var resp struct { + Dog struct { + Size struct { + Height int + Weight int + } + } + } + + c.MustPost(` + { + dog { + size { + height + } + ...AnimalWeight + } + } + fragment AnimalWeight on Animal { + size { + weight + } + } + `, &resp) + + require.Equal(t, 100, resp.Dog.Size.Height) + require.Equal(t, 35, resp.Dog.Size.Weight) + }) } diff --git a/codegen/testserver/singlefile/models-gen.go b/codegen/testserver/singlefile/models-gen.go index f02dcba4747..53afbdf3fd9 100644 --- a/codegen/testserver/singlefile/models-gen.go +++ b/codegen/testserver/singlefile/models-gen.go @@ -12,6 +12,7 @@ import ( type Animal interface { IsAnimal() GetSpecies() string + GetSize() *Size } type ContentChild interface { @@ -44,11 +45,13 @@ func (B) IsTestUnion() {} type Cat struct { Species string `json:"species"` + Size *Size `json:"size"` CatBreed string `json:"catBreed"` } func (Cat) IsAnimal() {} func (this Cat) GetSpecies() string { return this.Species } +func (this Cat) GetSize() *Size { return this.Size } type CheckIssue896 struct { ID *int `json:"id"` @@ -83,11 +86,13 @@ type DefaultParametersMirror struct { type Dog struct { Species string `json:"species"` + Size *Size `json:"size"` DogBreed string `json:"dogBreed"` } func (Dog) IsAnimal() {} func (this Dog) GetSpecies() string { return this.Species } +func (this Dog) GetSize() *Size { return this.Size } type EmbeddedDefaultScalar struct { Value *string `json:"value"` @@ -162,6 +167,11 @@ type Pet struct { Friends []*Pet `json:"friends"` } +type Size struct { + Height int `json:"height"` + Weight int `json:"weight"` +} + type Slices struct { Test1 []*string `json:"test1"` Test2 []string `json:"test2"` diff --git a/codegen/testserver/singlefile/resolver.go b/codegen/testserver/singlefile/resolver.go index d203976f6c8..fae59734b17 100644 --- a/codegen/testserver/singlefile/resolver.go +++ b/codegen/testserver/singlefile/resolver.go @@ -292,6 +292,11 @@ func (r *queryResolver) NotAnInterface(ctx context.Context) (BackedByInterface, panic("not implemented") } +// // foo +func (r *queryResolver) Dog(ctx context.Context) (*Dog, error) { + panic("not implemented") +} + // // foo func (r *queryResolver) Issue896a(ctx context.Context) ([]*CheckIssue896, error) { panic("not implemented") diff --git a/codegen/testserver/singlefile/stub.go b/codegen/testserver/singlefile/stub.go index d61151583fc..ad5e7b143ba 100644 --- a/codegen/testserver/singlefile/stub.go +++ b/codegen/testserver/singlefile/stub.go @@ -88,6 +88,7 @@ type Stub struct { NoShapeTypedNil func(ctx context.Context) (Shape, error) Animal func(ctx context.Context) (Animal, error) NotAnInterface func(ctx context.Context) (BackedByInterface, error) + Dog func(ctx context.Context) (*Dog, error) Issue896a func(ctx context.Context) ([]*CheckIssue896, error) MapStringInterface func(ctx context.Context, in map[string]interface{}) (map[string]interface{}, error) MapNestedStringInterface func(ctx context.Context, in *NestedMapInput) (map[string]interface{}, error) @@ -393,6 +394,9 @@ func (r *stubQuery) Animal(ctx context.Context) (Animal, error) { func (r *stubQuery) NotAnInterface(ctx context.Context) (BackedByInterface, error) { return r.QueryResolver.NotAnInterface(ctx) } +func (r *stubQuery) Dog(ctx context.Context) (*Dog, error) { + return r.QueryResolver.Dog(ctx) +} func (r *stubQuery) Issue896a(ctx context.Context) ([]*CheckIssue896, error) { return r.QueryResolver.Issue896a(ctx) } diff --git a/graphql/executable_schema.go b/graphql/executable_schema.go index 541b65fbeea..6189162288d 100644 --- a/graphql/executable_schema.go +++ b/graphql/executable_schema.go @@ -118,6 +118,11 @@ func getOrCreateAndAppendField(c *[]CollectedField, name string, alias string, o return &(*c)[i] } } + for _, ifc := range cf.ObjectDefinition.Interfaces { + if ifc == objectDefinition.Name { + return &(*c)[i] + } + } } }