diff --git a/codegen/testserver/followschema/interfaces.generated.go b/codegen/testserver/followschema/interfaces.generated.go index 5d139fc5d65..c5e71e7b506 100644 --- a/codegen/testserver/followschema/interfaces.generated.go +++ b/codegen/testserver/followschema/interfaces.generated.go @@ -249,6 +249,35 @@ func (ec *executionContext) _Circle_area(ctx context.Context, field graphql.Coll return ec.marshalOFloat2float64(ctx, field.Selections, res) } +func (ec *executionContext) _Circle_coordinates(ctx context.Context, field graphql.CollectedField, obj *Circle) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Circle", + Field: field, + Args: nil, + IsMethod: false, + IsResolver: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp := ec._fieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Coordinates, nil + }) + + if resTmp == nil { + return graphql.Null + } + res := resTmp.(Coordinates) + fc.Result = res + return ec.marshalOCoordinates2githubᚗcomᚋ99designsᚋgqlgenᚋcodegenᚋtestserverᚋfollowschemaᚐCoordinates(ctx, field.Selections, res) +} + func (ec *executionContext) _ConcreteNodeA_id(ctx context.Context, field graphql.CollectedField, obj *ConcreteNodeA) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { @@ -409,6 +438,70 @@ func (ec *executionContext) _ConcreteNodeInterface_child(ctx context.Context, fi return ec.marshalNNode2githubᚗcomᚋ99designsᚋgqlgenᚋcodegenᚋtestserverᚋfollowschemaᚐNode(ctx, field.Selections, res) } +func (ec *executionContext) _Coordinates_x(ctx context.Context, field graphql.CollectedField, obj *Coordinates) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Coordinates", + Field: field, + Args: nil, + IsMethod: false, + IsResolver: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp := ec._fieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.X, nil + }) + + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(float64) + fc.Result = res + return ec.marshalNFloat2float64(ctx, field.Selections, res) +} + +func (ec *executionContext) _Coordinates_y(ctx context.Context, field graphql.CollectedField, obj *Coordinates) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Coordinates", + Field: field, + Args: nil, + IsMethod: false, + IsResolver: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp := ec._fieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Y, nil + }) + + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(float64) + fc.Result = res + return ec.marshalNFloat2float64(ctx, field.Selections, res) +} + func (ec *executionContext) _Dog_species(ctx context.Context, field graphql.CollectedField, obj *Dog) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { @@ -560,6 +653,35 @@ func (ec *executionContext) _Rectangle_area(ctx context.Context, field graphql.C return ec.marshalOFloat2float64(ctx, field.Selections, res) } +func (ec *executionContext) _Rectangle_coordinates(ctx context.Context, field graphql.CollectedField, obj *Rectangle) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Rectangle", + Field: field, + Args: nil, + IsMethod: false, + IsResolver: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp := ec._fieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Coordinates, nil + }) + + if resTmp == nil { + return graphql.Null + } + res := resTmp.(Coordinates) + fc.Result = res + return ec.marshalOCoordinates2githubᚗcomᚋ99designsᚋgqlgenᚋcodegenᚋtestserverᚋfollowschemaᚐCoordinates(ctx, field.Selections, res) +} + // endregion **************************** field.gotpl ***************************** // region **************************** input.gotpl ***************************** @@ -778,6 +900,13 @@ func (ec *executionContext) _Circle(ctx context.Context, sel ast.SelectionSet, o out.Values[i] = innerFunc(ctx) + case "coordinates": + innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + return ec._Circle_coordinates(ctx, field, obj) + } + + out.Values[i] = innerFunc(ctx) + default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -881,6 +1010,47 @@ func (ec *executionContext) _ConcreteNodeInterface(ctx context.Context, sel ast. return out } +var coordinatesImplementors = []string{"Coordinates"} + +func (ec *executionContext) _Coordinates(ctx context.Context, sel ast.SelectionSet, obj *Coordinates) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, coordinatesImplementors) + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("Coordinates") + case "x": + innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + return ec._Coordinates_x(ctx, field, obj) + } + + out.Values[i] = innerFunc(ctx) + + if out.Values[i] == graphql.Null { + invalids++ + } + case "y": + innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + return ec._Coordinates_y(ctx, field, obj) + } + + out.Values[i] = innerFunc(ctx) + + 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 dogImplementors = []string{"Dog", "Animal"} func (ec *executionContext) _Dog(ctx context.Context, sel ast.SelectionSet, obj *Dog) graphql.Marshaler { @@ -953,6 +1123,13 @@ func (ec *executionContext) _Rectangle(ctx context.Context, sel ast.SelectionSet out.Values[i] = innerFunc(ctx) + case "coordinates": + innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + return ec._Rectangle_coordinates(ctx, field, obj) + } + + out.Values[i] = innerFunc(ctx) + default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -1009,6 +1186,10 @@ func (ec *executionContext) marshalOCircle2ᚖgithubᚗcomᚋ99designsᚋgqlgen return ec._Circle(ctx, sel, v) } +func (ec *executionContext) marshalOCoordinates2githubᚗcomᚋ99designsᚋgqlgenᚋcodegenᚋtestserverᚋfollowschemaᚐCoordinates(ctx context.Context, sel ast.SelectionSet, v Coordinates) graphql.Marshaler { + return ec._Coordinates(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.go b/codegen/testserver/followschema/interfaces.go index 39d908e4c24..7f06298b714 100644 --- a/codegen/testserver/followschema/interfaces.go +++ b/codegen/testserver/followschema/interfaces.go @@ -14,6 +14,7 @@ type ShapeUnion interface { type Circle struct { Radius float64 + Coordinates } func (c *Circle) Area() float64 { @@ -25,6 +26,7 @@ func (c *Circle) isShape() {} type Rectangle struct { Length, Width float64 + Coordinates } func (r *Rectangle) Area() float64 { @@ -79,6 +81,7 @@ type BackedByInterfaceImpl struct { func (b *BackedByInterfaceImpl) ThisShouldBind() string { return b.Value } + func (b *BackedByInterfaceImpl) ThisShouldBindWithError() (string, error) { return b.Value, b.Error } diff --git a/codegen/testserver/followschema/interfaces.graphql b/codegen/testserver/followschema/interfaces.graphql index 4dc320f9a29..af5c17170c3 100644 --- a/codegen/testserver/followschema/interfaces.graphql +++ b/codegen/testserver/followschema/interfaces.graphql @@ -27,19 +27,27 @@ type Cat implements Animal { catBreed: String! } +type Coordinates { + x: Float! + y: Float! +} interface Shape { area: Float + coordinates: Coordinates } + type Circle implements Shape { radius: Float area: Float + coordinates: Coordinates } type Rectangle implements Shape { length: Float width: Float area: Float + coordinates: Coordinates } -union ShapeUnion @goModel(model:"followschema.ShapeUnion") = Circle | Rectangle +union ShapeUnion @goModel(model: "followschema.ShapeUnion") = Circle | Rectangle directive @makeNil on FIELD_DEFINITION directive @makeTypedNil on FIELD_DEFINITION @@ -55,7 +63,7 @@ type ConcreteNodeA implements Node { name: String! } -""" Implements the Node interface with another interface """ +" Implements the Node interface with another interface " type ConcreteNodeInterface implements Node { id: ID! child: Node! diff --git a/codegen/testserver/followschema/interfaces_test.go b/codegen/testserver/followschema/interfaces_test.go index c2fae2ff9ad..d67c7da59f4 100644 --- a/codegen/testserver/followschema/interfaces_test.go +++ b/codegen/testserver/followschema/interfaces_test.go @@ -197,4 +197,60 @@ func TestInterfaces(t *testing.T) { require.Equal(t, "CNII", resp.Node.ID) require.Equal(t, "Child", resp.Node.Child.ID) }) + + t.Run("interface implementors should return merged base fields", func(t *testing.T) { + resolvers := &Stub{} + resolvers.QueryResolver.Shapes = func(ctx context.Context) (shapes []Shape, err error) { + return []Shape{ + &Rectangle{ + Coordinates: Coordinates{ + X: -1, + Y: -1, + }, + }, + &Circle{ + Coordinates: Coordinates{ + X: 1, + Y: 1, + }, + }, + }, nil + } + + c := client.New(handler.NewDefaultServer(NewExecutableSchema(Config{Resolvers: resolvers}))) + var resp struct { + Shapes []struct { + Coordinates struct { + X float64 + Y float64 + } + } + } + + c.MustPost(` + { + shapes { + coordinates { + x + } + ... on Rectangle { + coordinates { + x + } + } + ... on Circle { + coordinates { + y + } + } + } + } + `, &resp) + + require.Equal(t, 2, len(resp.Shapes)) + require.Equal(t, float64(-1), resp.Shapes[0].Coordinates.X) + require.Equal(t, float64(0), resp.Shapes[0].Coordinates.Y) + require.Equal(t, float64(1), resp.Shapes[1].Coordinates.X) + require.Equal(t, float64(1), resp.Shapes[1].Coordinates.Y) + }) } diff --git a/codegen/testserver/followschema/models-gen.go b/codegen/testserver/followschema/models-gen.go index 7e24f2aa394..6ddd209366c 100644 --- a/codegen/testserver/followschema/models-gen.go +++ b/codegen/testserver/followschema/models-gen.go @@ -64,6 +64,11 @@ type ContentUser struct { func (ContentUser) IsContentChild() {} +type Coordinates struct { + X float64 `json:"x"` + Y float64 `json:"y"` +} + type DefaultInput struct { FalsyBoolean *bool `json:"falsyBoolean"` TruthyBoolean *bool `json:"truthyBoolean"` diff --git a/codegen/testserver/followschema/prelude.generated.go b/codegen/testserver/followschema/prelude.generated.go index 2a306d304e2..0109bde4739 100644 --- a/codegen/testserver/followschema/prelude.generated.go +++ b/codegen/testserver/followschema/prelude.generated.go @@ -1511,6 +1511,21 @@ func (ec *executionContext) marshalNBoolean2bool(ctx context.Context, sel ast.Se return res } +func (ec *executionContext) unmarshalNFloat2float64(ctx context.Context, v interface{}) (float64, error) { + res, err := graphql.UnmarshalFloat(v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNFloat2float64(ctx context.Context, sel ast.SelectionSet, v float64) graphql.Marshaler { + res := graphql.MarshalFloat(v) + if res == graphql.Null { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "must not be null") + } + } + return res +} + func (ec *executionContext) unmarshalNID2int(ctx context.Context, v interface{}) (int, error) { res, err := graphql.UnmarshalIntID(v) return res, graphql.ErrorOnPath(ctx, err) diff --git a/codegen/testserver/followschema/root!.generated.go b/codegen/testserver/followschema/root!.generated.go index c5bd0aeb35e..69079b433cf 100644 --- a/codegen/testserver/followschema/root!.generated.go +++ b/codegen/testserver/followschema/root!.generated.go @@ -102,8 +102,9 @@ type ComplexityRoot struct { } Circle struct { - Area func(childComplexity int) int - Radius func(childComplexity int) int + Area func(childComplexity int) int + Coordinates func(childComplexity int) int + Radius func(childComplexity int) int } ConcreteNodeA struct { @@ -125,6 +126,11 @@ type ComplexityRoot struct { Foo func(childComplexity int) int } + Coordinates struct { + X func(childComplexity int) int + Y func(childComplexity int) int + } + DefaultParametersMirror struct { FalsyBoolean func(childComplexity int) int TruthyBoolean func(childComplexity int) int @@ -333,9 +339,10 @@ type ComplexityRoot struct { } Rectangle struct { - Area func(childComplexity int) int - Length func(childComplexity int) int - Width func(childComplexity int) int + Area func(childComplexity int) int + Coordinates func(childComplexity int) int + Length func(childComplexity int) int + Width func(childComplexity int) int } Slices struct { @@ -534,6 +541,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Circle.Area(childComplexity), true + case "Circle.coordinates": + if e.complexity.Circle.Coordinates == nil { + break + } + + return e.complexity.Circle.Coordinates(childComplexity), true + case "Circle.radius": if e.complexity.Circle.Radius == nil { break @@ -590,6 +604,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Content_User.Foo(childComplexity), true + case "Coordinates.x": + if e.complexity.Coordinates.X == nil { + break + } + + return e.complexity.Coordinates.X(childComplexity), true + + case "Coordinates.y": + if e.complexity.Coordinates.Y == nil { + break + } + + return e.complexity.Coordinates.Y(childComplexity), true + case "DefaultParametersMirror.falsyBoolean": if e.complexity.DefaultParametersMirror.FalsyBoolean == nil { break @@ -1536,6 +1564,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Rectangle.Area(childComplexity), true + case "Rectangle.coordinates": + if e.complexity.Rectangle.Coordinates == nil { + break + } + + return e.complexity.Rectangle.Coordinates(childComplexity), true + case "Rectangle.length": if e.complexity.Rectangle.Length == nil { break @@ -2021,19 +2056,27 @@ type Cat implements Animal { catBreed: String! } +type Coordinates { + x: Float! + y: Float! +} interface Shape { area: Float + coordinates: Coordinates } + type Circle implements Shape { radius: Float area: Float + coordinates: Coordinates } type Rectangle implements Shape { length: Float width: Float area: Float + coordinates: Coordinates } -union ShapeUnion @goModel(model:"followschema.ShapeUnion") = Circle | Rectangle +union ShapeUnion @goModel(model: "followschema.ShapeUnion") = Circle | Rectangle directive @makeNil on FIELD_DEFINITION directive @makeTypedNil on FIELD_DEFINITION @@ -2049,7 +2092,7 @@ type ConcreteNodeA implements Node { name: String! } -""" Implements the Node interface with another interface """ +" Implements the Node interface with another interface " type ConcreteNodeInterface implements Node { id: ID! child: Node! diff --git a/codegen/testserver/singlefile/generated.go b/codegen/testserver/singlefile/generated.go index 768095f2535..75724eaecbd 100644 --- a/codegen/testserver/singlefile/generated.go +++ b/codegen/testserver/singlefile/generated.go @@ -113,8 +113,9 @@ type ComplexityRoot struct { } Circle struct { - Area func(childComplexity int) int - Radius func(childComplexity int) int + Area func(childComplexity int) int + Coordinates func(childComplexity int) int + Radius func(childComplexity int) int } ConcreteNodeA struct { @@ -136,6 +137,11 @@ type ComplexityRoot struct { Foo func(childComplexity int) int } + Coordinates struct { + X func(childComplexity int) int + Y func(childComplexity int) int + } + DefaultParametersMirror struct { FalsyBoolean func(childComplexity int) int TruthyBoolean func(childComplexity int) int @@ -344,9 +350,10 @@ type ComplexityRoot struct { } Rectangle struct { - Area func(childComplexity int) int - Length func(childComplexity int) int - Width func(childComplexity int) int + Area func(childComplexity int) int + Coordinates func(childComplexity int) int + Length func(childComplexity int) int + Width func(childComplexity int) int } Slices struct { @@ -664,6 +671,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Circle.Area(childComplexity), true + case "Circle.coordinates": + if e.complexity.Circle.Coordinates == nil { + break + } + + return e.complexity.Circle.Coordinates(childComplexity), true + case "Circle.radius": if e.complexity.Circle.Radius == nil { break @@ -720,6 +734,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Content_User.Foo(childComplexity), true + case "Coordinates.x": + if e.complexity.Coordinates.X == nil { + break + } + + return e.complexity.Coordinates.X(childComplexity), true + + case "Coordinates.y": + if e.complexity.Coordinates.Y == nil { + break + } + + return e.complexity.Coordinates.Y(childComplexity), true + case "DefaultParametersMirror.falsyBoolean": if e.complexity.DefaultParametersMirror.FalsyBoolean == nil { break @@ -1666,6 +1694,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Rectangle.Area(childComplexity), true + case "Rectangle.coordinates": + if e.complexity.Rectangle.Coordinates == nil { + break + } + + return e.complexity.Rectangle.Coordinates(childComplexity), true + case "Rectangle.length": if e.complexity.Rectangle.Length == nil { break @@ -2151,19 +2186,27 @@ type Cat implements Animal { catBreed: String! } +type Coordinates { + x: Float! + y: Float! +} interface Shape { area: Float + coordinates: Coordinates } + type Circle implements Shape { radius: Float area: Float + coordinates: Coordinates } type Rectangle implements Shape { length: Float width: Float area: Float + coordinates: Coordinates } -union ShapeUnion @goModel(model:"singlefile.ShapeUnion") = Circle | Rectangle +union ShapeUnion @goModel(model: "singlefile.ShapeUnion") = Circle | Rectangle directive @makeNil on FIELD_DEFINITION directive @makeTypedNil on FIELD_DEFINITION @@ -2179,7 +2222,7 @@ type ConcreteNodeA implements Node { name: String! } -""" Implements the Node interface with another interface """ +" Implements the Node interface with another interface " type ConcreteNodeInterface implements Node { id: ID! child: Node! @@ -4217,6 +4260,35 @@ func (ec *executionContext) _Circle_area(ctx context.Context, field graphql.Coll return ec.marshalOFloat2float64(ctx, field.Selections, res) } +func (ec *executionContext) _Circle_coordinates(ctx context.Context, field graphql.CollectedField, obj *Circle) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Circle", + Field: field, + Args: nil, + IsMethod: false, + IsResolver: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp := ec._fieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Coordinates, nil + }) + + if resTmp == nil { + return graphql.Null + } + res := resTmp.(Coordinates) + fc.Result = res + return ec.marshalOCoordinates2githubᚗcomᚋ99designsᚋgqlgenᚋcodegenᚋtestserverᚋsinglefileᚐCoordinates(ctx, field.Selections, res) +} + func (ec *executionContext) _ConcreteNodeA_id(ctx context.Context, field graphql.CollectedField, obj *ConcreteNodeA) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { @@ -4435,6 +4507,70 @@ func (ec *executionContext) _Content_User_foo(ctx context.Context, field graphql return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } +func (ec *executionContext) _Coordinates_x(ctx context.Context, field graphql.CollectedField, obj *Coordinates) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Coordinates", + Field: field, + Args: nil, + IsMethod: false, + IsResolver: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp := ec._fieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.X, nil + }) + + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(float64) + fc.Result = res + return ec.marshalNFloat2float64(ctx, field.Selections, res) +} + +func (ec *executionContext) _Coordinates_y(ctx context.Context, field graphql.CollectedField, obj *Coordinates) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Coordinates", + Field: field, + Args: nil, + IsMethod: false, + IsResolver: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp := ec._fieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Y, nil + }) + + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(float64) + fc.Result = res + return ec.marshalNFloat2float64(ctx, field.Selections, res) +} + func (ec *executionContext) _DefaultParametersMirror_falsyBoolean(ctx context.Context, field graphql.CollectedField, obj *DefaultParametersMirror) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { @@ -8676,6 +8812,35 @@ func (ec *executionContext) _Rectangle_area(ctx context.Context, field graphql.C return ec.marshalOFloat2float64(ctx, field.Selections, res) } +func (ec *executionContext) _Rectangle_coordinates(ctx context.Context, field graphql.CollectedField, obj *Rectangle) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Rectangle", + Field: field, + Args: nil, + IsMethod: false, + IsResolver: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp := ec._fieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Coordinates, nil + }) + + if resTmp == nil { + return graphql.Null + } + res := resTmp.(Coordinates) + fc.Result = res + return ec.marshalOCoordinates2githubᚗcomᚋ99designsᚋgqlgenᚋcodegenᚋtestserverᚋsinglefileᚐCoordinates(ctx, field.Selections, res) +} + func (ec *executionContext) _Slices_test1(ctx context.Context, field graphql.CollectedField, obj *Slices) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { @@ -11957,6 +12122,13 @@ func (ec *executionContext) _Circle(ctx context.Context, sel ast.SelectionSet, o out.Values[i] = innerFunc(ctx) + case "coordinates": + innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + return ec._Circle_coordinates(ctx, field, obj) + } + + out.Values[i] = innerFunc(ctx) + default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -12116,6 +12288,47 @@ func (ec *executionContext) _Content_User(ctx context.Context, sel ast.Selection return out } +var coordinatesImplementors = []string{"Coordinates"} + +func (ec *executionContext) _Coordinates(ctx context.Context, sel ast.SelectionSet, obj *Coordinates) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, coordinatesImplementors) + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("Coordinates") + case "x": + innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + return ec._Coordinates_x(ctx, field, obj) + } + + out.Values[i] = innerFunc(ctx) + + if out.Values[i] == graphql.Null { + invalids++ + } + case "y": + innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + return ec._Coordinates_y(ctx, field, obj) + } + + out.Values[i] = innerFunc(ctx) + + 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 defaultParametersMirrorImplementors = []string{"DefaultParametersMirror"} func (ec *executionContext) _DefaultParametersMirror(ctx context.Context, sel ast.SelectionSet, obj *DefaultParametersMirror) graphql.Marshaler { @@ -14777,6 +14990,13 @@ func (ec *executionContext) _Rectangle(ctx context.Context, sel ast.SelectionSet out.Values[i] = innerFunc(ctx) + case "coordinates": + innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + return ec._Rectangle_coordinates(ctx, field, obj) + } + + out.Values[i] = innerFunc(ctx) + default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -15843,6 +16063,21 @@ func (ec *executionContext) marshalNFallbackToStringEncoding2githubᚗcomᚋ99de return res } +func (ec *executionContext) unmarshalNFloat2float64(ctx context.Context, v interface{}) (float64, error) { + res, err := graphql.UnmarshalFloat(v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNFloat2float64(ctx context.Context, sel ast.SelectionSet, v float64) graphql.Marshaler { + res := graphql.MarshalFloat(v) + if res == graphql.Null { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "must not be null") + } + } + return res +} + func (ec *executionContext) unmarshalNID2int(ctx context.Context, v interface{}) (int, error) { res, err := graphql.UnmarshalIntID(v) return res, graphql.ErrorOnPath(ctx, err) @@ -16829,6 +17064,10 @@ func (ec *executionContext) marshalOCircle2ᚖgithubᚗcomᚋ99designsᚋgqlgen return ec._Circle(ctx, sel, v) } +func (ec *executionContext) marshalOCoordinates2githubᚗcomᚋ99designsᚋgqlgenᚋcodegenᚋtestserverᚋsinglefileᚐCoordinates(ctx context.Context, sel ast.SelectionSet, v Coordinates) graphql.Marshaler { + return ec._Coordinates(ctx, sel, &v) +} + func (ec *executionContext) unmarshalODefaultScalarImplementation2ᚖstring(ctx context.Context, v interface{}) (*string, error) { if v == nil { return nil, nil diff --git a/codegen/testserver/singlefile/interfaces.go b/codegen/testserver/singlefile/interfaces.go index 9fd2ae2e2bf..1115fabdbac 100644 --- a/codegen/testserver/singlefile/interfaces.go +++ b/codegen/testserver/singlefile/interfaces.go @@ -14,6 +14,7 @@ type ShapeUnion interface { type Circle struct { Radius float64 + Coordinates } func (c *Circle) Area() float64 { @@ -25,6 +26,7 @@ func (c *Circle) isShape() {} type Rectangle struct { Length, Width float64 + Coordinates } func (r *Rectangle) Area() float64 { @@ -79,6 +81,7 @@ type BackedByInterfaceImpl struct { func (b *BackedByInterfaceImpl) ThisShouldBind() string { return b.Value } + func (b *BackedByInterfaceImpl) ThisShouldBindWithError() (string, error) { return b.Value, b.Error } diff --git a/codegen/testserver/singlefile/interfaces.graphql b/codegen/testserver/singlefile/interfaces.graphql index 1c138f595aa..4b6480be83c 100644 --- a/codegen/testserver/singlefile/interfaces.graphql +++ b/codegen/testserver/singlefile/interfaces.graphql @@ -27,19 +27,27 @@ type Cat implements Animal { catBreed: String! } +type Coordinates { + x: Float! + y: Float! +} interface Shape { area: Float + coordinates: Coordinates } + type Circle implements Shape { radius: Float area: Float + coordinates: Coordinates } type Rectangle implements Shape { length: Float width: Float area: Float + coordinates: Coordinates } -union ShapeUnion @goModel(model:"singlefile.ShapeUnion") = Circle | Rectangle +union ShapeUnion @goModel(model: "singlefile.ShapeUnion") = Circle | Rectangle directive @makeNil on FIELD_DEFINITION directive @makeTypedNil on FIELD_DEFINITION @@ -55,7 +63,7 @@ type ConcreteNodeA implements Node { name: String! } -""" Implements the Node interface with another interface """ +" Implements the Node interface with another interface " type ConcreteNodeInterface implements Node { id: ID! child: Node! diff --git a/codegen/testserver/singlefile/interfaces_test.go b/codegen/testserver/singlefile/interfaces_test.go index 4cdcecbc500..6093a8d7024 100644 --- a/codegen/testserver/singlefile/interfaces_test.go +++ b/codegen/testserver/singlefile/interfaces_test.go @@ -197,4 +197,60 @@ func TestInterfaces(t *testing.T) { require.Equal(t, "CNII", resp.Node.ID) require.Equal(t, "Child", resp.Node.Child.ID) }) + + t.Run("interface implementors should return merged base fields", func(t *testing.T) { + resolvers := &Stub{} + resolvers.QueryResolver.Shapes = func(ctx context.Context) (shapes []Shape, err error) { + return []Shape{ + &Rectangle{ + Coordinates: Coordinates{ + X: -1, + Y: -1, + }, + }, + &Circle{ + Coordinates: Coordinates{ + X: 1, + Y: 1, + }, + }, + }, nil + } + + c := client.New(handler.NewDefaultServer(NewExecutableSchema(Config{Resolvers: resolvers}))) + var resp struct { + Shapes []struct { + Coordinates struct { + X float64 + Y float64 + } + } + } + + c.MustPost(` + { + shapes { + coordinates { + x + } + ... on Rectangle { + coordinates { + x + } + } + ... on Circle { + coordinates { + y + } + } + } + } + `, &resp) + + require.Equal(t, 2, len(resp.Shapes)) + require.Equal(t, float64(-1), resp.Shapes[0].Coordinates.X) + require.Equal(t, float64(0), resp.Shapes[0].Coordinates.Y) + require.Equal(t, float64(1), resp.Shapes[1].Coordinates.X) + require.Equal(t, float64(1), resp.Shapes[1].Coordinates.Y) + }) } diff --git a/codegen/testserver/singlefile/models-gen.go b/codegen/testserver/singlefile/models-gen.go index 0bd3568b5a7..6e59b20b8b8 100644 --- a/codegen/testserver/singlefile/models-gen.go +++ b/codegen/testserver/singlefile/models-gen.go @@ -64,6 +64,11 @@ type ContentUser struct { func (ContentUser) IsContentChild() {} +type Coordinates struct { + X float64 `json:"x"` + Y float64 `json:"y"` +} + type DefaultInput struct { FalsyBoolean *bool `json:"falsyBoolean"` TruthyBoolean *bool `json:"truthyBoolean"` diff --git a/graphql/executable_schema.go b/graphql/executable_schema.go index 012baafff48..541b65fbeea 100644 --- a/graphql/executable_schema.go +++ b/graphql/executable_schema.go @@ -37,6 +37,7 @@ func collectFields(reqCtx *OperationContext, selSet ast.SelectionSet, satisfies }) f.Selections = append(f.Selections, sel.SelectionSet...) + case *ast.InlineFragment: if !shouldIncludeNode(sel.Directives, reqCtx.Variables) { continue @@ -73,6 +74,7 @@ func collectFields(reqCtx *OperationContext, selSet ast.SelectionSet, satisfies f := getOrCreateAndAppendField(&groupedFields, childField.Name, childField.Alias, childField.ObjectDefinition, func() CollectedField { return childField }) f.Selections = append(f.Selections, childField.Selections...) } + default: panic(fmt.Errorf("unsupported %T", sel)) } @@ -98,8 +100,24 @@ func instanceOf(val string, satisfies []string) bool { func getOrCreateAndAppendField(c *[]CollectedField, name string, alias string, objectDefinition *ast.Definition, creator func() CollectedField) *CollectedField { for i, cf := range *c { - if cf.Name == name && cf.Alias == alias && (cf.ObjectDefinition == objectDefinition || (cf.ObjectDefinition != nil && objectDefinition != nil && cf.ObjectDefinition.Name == objectDefinition.Name)) { - return &(*c)[i] + if cf.Name == name && cf.Alias == alias { + if cf.ObjectDefinition == objectDefinition { + return &(*c)[i] + } + + if cf.ObjectDefinition == nil || objectDefinition == nil { + continue + } + + if cf.ObjectDefinition.Name == objectDefinition.Name { + return &(*c)[i] + } + + for _, ifc := range objectDefinition.Interfaces { + if ifc == cf.ObjectDefinition.Name { + return &(*c)[i] + } + } } }