diff --git a/execution/engine/federation_integration_test.go b/execution/engine/federation_integration_test.go index 1b867bb37..b75ccd8a5 100644 --- a/execution/engine/federation_integration_test.go +++ b/execution/engine/federation_integration_test.go @@ -570,6 +570,17 @@ func TestFederationIntegrationTest(t *testing.T) { expected := `{"data":{"cat":{"name":"Pepper"},"me":{"id":"1234","username":"Me","realName":"User Usington","reviews":[{"body":"A highly effective form of birth control."},{"body":"Fedoras are one of the most fashionable hats around and can look great with a variety of outfits."}],"history":[{},{"rating":5},{}]}}}` assert.Equal(t, compact(expected), string(resp)) }) + + t.Run("merge concrete type in root field and interface fragment", func(t *testing.T) { + setup := federationtesting.NewFederationSetup(addGateway(false)) + t.Cleanup(setup.Close) + gqlClient := NewGraphqlClient(http.DefaultClient) + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) + resp := gqlClient.Query(ctx, setup.GatewayServer.URL, testQueryPath("queries/merge_concrete_type_in_root_field_and_interface_fragment.graphql"), nil, t) + expected := `{"data":{"productA":{"category":{"id":"c111","displayOwner":{"name":"owner"}}}}}` + assert.Equal(t, compact(expected), string(resp)) + }) } func compact(input string) string { diff --git a/execution/federationtesting/accounts/graph/generated/generated.go b/execution/federationtesting/accounts/graph/generated/generated.go index 22fd02edc..19c06c1fc 100644 --- a/execution/federationtesting/accounts/graph/generated/generated.go +++ b/execution/federationtesting/accounts/graph/generated/generated.go @@ -69,6 +69,11 @@ type ComplexityRoot struct { Name func(childComplexity int) int } + Category struct { + ID func(childComplexity int) int + Owner func(childComplexity int) int + } + ConcreteListItem1 struct { Obj func(childComplexity int) int } @@ -85,10 +90,24 @@ type ComplexityRoot struct { FindUserByID func(childComplexity int, id string) int } + Owner struct { + Name func(childComplexity int) int + } + Product struct { Upc func(childComplexity int) int } + ProductA struct { + Category func(childComplexity int) int + ID func(childComplexity int) int + } + + ProductB struct { + Category func(childComplexity int) int + ID func(childComplexity int) int + } + Purchase struct { Product func(childComplexity int) int Quantity func(childComplexity int) int @@ -104,6 +123,7 @@ type ComplexityRoot struct { InterfaceUnion func(childComplexity int, which model.Which) int Me func(childComplexity int) int OtherInterfaces func(childComplexity int) int + ProductA func(childComplexity int) int SomeNestedInterfaces func(childComplexity int) int TitleName func(childComplexity int) int __resolve__service func(childComplexity int) int @@ -194,6 +214,7 @@ type QueryResolver interface { Cds(ctx context.Context) ([]model.Cd, error) OtherInterfaces(ctx context.Context) ([]model.SomeInterface, error) SomeNestedInterfaces(ctx context.Context) ([]model.SomeNestedInterface, error) + ProductA(ctx context.Context) (*model.ProductA, error) } type executableSchema struct { @@ -264,6 +285,20 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.complexity.Cat.Name(childComplexity), true + case "Category.id": + if e.complexity.Category.ID == nil { + break + } + + return e.complexity.Category.ID(childComplexity), true + + case "Category.owner": + if e.complexity.Category.Owner == nil { + break + } + + return e.complexity.Category.Owner(childComplexity), true + case "ConcreteListItem1.obj": if e.complexity.ConcreteListItem1.Obj == nil { break @@ -297,6 +332,13 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.complexity.Entity.FindUserByID(childComplexity, args["id"].(string)), true + case "Owner.name": + if e.complexity.Owner.Name == nil { + break + } + + return e.complexity.Owner.Name(childComplexity), true + case "Product.upc": if e.complexity.Product.Upc == nil { break @@ -304,6 +346,34 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.complexity.Product.Upc(childComplexity), true + case "ProductA.category": + if e.complexity.ProductA.Category == nil { + break + } + + return e.complexity.ProductA.Category(childComplexity), true + + case "ProductA.id": + if e.complexity.ProductA.ID == nil { + break + } + + return e.complexity.ProductA.ID(childComplexity), true + + case "ProductB.category": + if e.complexity.ProductB.Category == nil { + break + } + + return e.complexity.ProductB.Category(childComplexity), true + + case "ProductB.id": + if e.complexity.ProductB.ID == nil { + break + } + + return e.complexity.ProductB.ID(childComplexity), true + case "Purchase.product": if e.complexity.Purchase.Product == nil { break @@ -386,6 +456,13 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.complexity.Query.OtherInterfaces(childComplexity), true + case "Query.productA": + if e.complexity.Query.ProductA == nil { + break + } + + return e.complexity.Query.ProductA(childComplexity), true + case "Query.someNestedInterfaces": if e.complexity.Query.SomeNestedInterfaces == nil { break @@ -752,6 +829,7 @@ var sources = []*ast.Source{ cds: [CD] otherInterfaces: [SomeInterface] someNestedInterfaces: [SomeNestedInterface] + productA: ProductA } type Cat { @@ -922,7 +1000,28 @@ type CDerObj { first: String! middle: String! last: String! -}`, BuiltIn: false}, +} + +interface Base { + category: Category +} +type Category { + id: ID! + owner: Owner +} +type Owner { + name: String! +} + +type ProductA implements Base { + id: ID! + category: Category +} +type ProductB implements Base { + id: ID! + category: Category +} +`, BuiltIn: false}, {Name: "../../federation/directives.graphql", Input: ` directive @key(fields: _FieldSet!) repeatable on OBJECT | INTERFACE directive @requires(fields: _FieldSet!) on FIELD_DEFINITION @@ -1502,6 +1601,95 @@ func (ec *executionContext) fieldContext_Cat_name(_ context.Context, field graph return fc, nil } +func (ec *executionContext) _Category_id(ctx context.Context, field graphql.CollectedField, obj *model.Category) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Category_id(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, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.ID, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNID2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Category_id(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Category", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type ID does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Category_owner(ctx context.Context, field graphql.CollectedField, obj *model.Category) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Category_owner(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, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.Owner, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*model.Owner) + fc.Result = res + return ec.marshalOOwner2ᚖgithub.comᚋwundergraphᚋgraphqlᚑgoᚑtoolsᚋexecutionᚋfederationtestingᚋaccountsᚋgraphᚋmodelᚐOwner(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Category_owner(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Category", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "name": + return ec.fieldContext_Owner_name(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Owner", field.Name) + }, + } + return fc, nil +} + func (ec *executionContext) _ConcreteListItem1_obj(ctx context.Context, field graphql.CollectedField, obj *model.ConcreteListItem1) (ret graphql.Marshaler) { fc, err := ec.fieldContext_ConcreteListItem1_obj(ctx, field) if err != nil { @@ -1677,35 +1865,258 @@ func (ec *executionContext) fieldContext_Entity_findUserByID(ctx context.Context IsMethod: true, IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "id": - return ec.fieldContext_User_id(ctx, field) - case "username": - return ec.fieldContext_User_username(ctx, field) - case "history": - return ec.fieldContext_User_history(ctx, field) - case "realName": - return ec.fieldContext_User_realName(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type User", field.Name) + switch field.Name { + case "id": + return ec.fieldContext_User_id(ctx, field) + case "username": + return ec.fieldContext_User_username(ctx, field) + case "history": + return ec.fieldContext_User_history(ctx, field) + case "realName": + return ec.fieldContext_User_realName(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type User", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Entity_findUserByID_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Owner_name(ctx context.Context, field graphql.CollectedField, obj *model.Owner) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Owner_name(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, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.Name, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Owner_name(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Owner", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Product_upc(ctx context.Context, field graphql.CollectedField, obj *model.Product) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Product_upc(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, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.Upc, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Product_upc(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Product", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _ProductA_id(ctx context.Context, field graphql.CollectedField, obj *model.ProductA) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ProductA_id(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, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.ID, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNID2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_ProductA_id(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ProductA", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type ID does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _ProductA_category(ctx context.Context, field graphql.CollectedField, obj *model.ProductA) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ProductA_category(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, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.Category, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*model.Category) + fc.Result = res + return ec.marshalOCategory2ᚖgithub.comᚋwundergraphᚋgraphqlᚑgoᚑtoolsᚋexecutionᚋfederationtestingᚋaccountsᚋgraphᚋmodelᚐCategory(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_ProductA_category(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ProductA", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_Category_id(ctx, field) + case "owner": + return ec.fieldContext_Category_owner(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Category", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _ProductB_id(ctx context.Context, field graphql.CollectedField, obj *model.ProductB) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ProductB_id(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, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.ID, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNID2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_ProductB_id(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ProductB", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type ID does not have child fields") }, } - defer func() { - if r := recover(); r != nil { - err = ec.Recover(ctx, r) - ec.Error(ctx, err) - } - }() - ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Entity_findUserByID_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { - ec.Error(ctx, err) - return fc, err - } return fc, nil } -func (ec *executionContext) _Product_upc(ctx context.Context, field graphql.CollectedField, obj *model.Product) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Product_upc(ctx, field) +func (ec *executionContext) _ProductB_category(ctx context.Context, field graphql.CollectedField, obj *model.ProductB) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ProductB_category(ctx, field) if err != nil { return graphql.Null } @@ -1718,31 +2129,34 @@ func (ec *executionContext) _Product_upc(ctx context.Context, field graphql.Coll }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { ctx = rctx // use context from middleware stack in children - return obj.Upc, nil + return obj.Category, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.(string) + res := resTmp.(*model.Category) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalOCategory2ᚖgithub.comᚋwundergraphᚋgraphqlᚑgoᚑtoolsᚋexecutionᚋfederationtestingᚋaccountsᚋgraphᚋmodelᚐCategory(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Product_upc(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_ProductB_category(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Product", + Object: "ProductB", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + switch field.Name { + case "id": + return ec.fieldContext_Category_id(ctx, field) + case "owner": + return ec.fieldContext_Category_owner(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Category", field.Name) }, } return fc, nil @@ -2328,6 +2742,53 @@ func (ec *executionContext) fieldContext_Query_someNestedInterfaces(_ context.Co return fc, nil } +func (ec *executionContext) _Query_productA(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_productA(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, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().ProductA(rctx) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*model.ProductA) + fc.Result = res + return ec.marshalOProductA2ᚖgithub.comᚋwundergraphᚋgraphqlᚑgoᚑtoolsᚋexecutionᚋfederationtestingᚋaccountsᚋgraphᚋmodelᚐProductA(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_productA(_ 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 "id": + return ec.fieldContext_ProductA_id(ctx, field) + case "category": + return ec.fieldContext_ProductA_category(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type ProductA", field.Name) + }, + } + return fc, nil +} + func (ec *executionContext) _Query__entities(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Query__entities(ctx, field) if err != nil { @@ -6034,6 +6495,29 @@ func (ec *executionContext) _AbstractListItem(ctx context.Context, sel ast.Selec } } +func (ec *executionContext) _Base(ctx context.Context, sel ast.SelectionSet, obj model.Base) graphql.Marshaler { + switch obj := (obj).(type) { + case nil: + return graphql.Null + case model.ProductB: + return ec._ProductB(ctx, sel, &obj) + case *model.ProductB: + if obj == nil { + return graphql.Null + } + return ec._ProductB(ctx, sel, obj) + case model.ProductA: + return ec._ProductA(ctx, sel, &obj) + case *model.ProductA: + if obj == nil { + return graphql.Null + } + return ec._ProductA(ctx, sel, obj) + default: + panic(fmt.Errorf("unexpected type %T", obj)) + } +} + func (ec *executionContext) _CD(ctx context.Context, sel ast.SelectionSet, obj model.Cd) graphql.Marshaler { switch obj := (obj).(type) { case nil: @@ -6534,6 +7018,47 @@ func (ec *executionContext) _Cat(ctx context.Context, sel ast.SelectionSet, obj return out } +var categoryImplementors = []string{"Category"} + +func (ec *executionContext) _Category(ctx context.Context, sel ast.SelectionSet, obj *model.Category) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, categoryImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("Category") + case "id": + out.Values[i] = ec._Category_id(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "owner": + out.Values[i] = ec._Category_owner(ctx, field, obj) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + var concreteListItem1Implementors = []string{"ConcreteListItem1", "AbstractListItem"} func (ec *executionContext) _ConcreteListItem1(ctx context.Context, sel ast.SelectionSet, obj *model.ConcreteListItem1) graphql.Marshaler { @@ -6712,6 +7237,45 @@ func (ec *executionContext) _Entity(ctx context.Context, sel ast.SelectionSet) g return out } +var ownerImplementors = []string{"Owner"} + +func (ec *executionContext) _Owner(ctx context.Context, sel ast.SelectionSet, obj *model.Owner) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, ownerImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("Owner") + case "name": + out.Values[i] = ec._Owner_name(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + var productImplementors = []string{"Product", "_Entity"} func (ec *executionContext) _Product(ctx context.Context, sel ast.SelectionSet, obj *model.Product) graphql.Marshaler { @@ -6751,6 +7315,88 @@ func (ec *executionContext) _Product(ctx context.Context, sel ast.SelectionSet, return out } +var productAImplementors = []string{"ProductA", "Base"} + +func (ec *executionContext) _ProductA(ctx context.Context, sel ast.SelectionSet, obj *model.ProductA) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, productAImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("ProductA") + case "id": + out.Values[i] = ec._ProductA_id(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "category": + out.Values[i] = ec._ProductA_category(ctx, field, obj) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var productBImplementors = []string{"ProductB", "Base"} + +func (ec *executionContext) _ProductB(ctx context.Context, sel ast.SelectionSet, obj *model.ProductB) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, productBImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("ProductB") + case "id": + out.Values[i] = ec._ProductB_id(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "category": + out.Values[i] = ec._ProductB_category(ctx, field, obj) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + var purchaseImplementors = []string{"Purchase", "History", "Info"} func (ec *executionContext) _Purchase(ctx context.Context, sel ast.SelectionSet, obj *model.Purchase) graphql.Marshaler { @@ -7005,6 +7651,25 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "productA": + field := field + + innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_productA(ctx, field) + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "_entities": field := field @@ -8708,6 +9373,13 @@ func (ec *executionContext) marshalOCat2ᚖgithub.comᚋwundergraphᚋgraphql return ec._Cat(ctx, sel, v) } +func (ec *executionContext) marshalOCategory2ᚖgithub.comᚋwundergraphᚋgraphqlᚑgoᚑtoolsᚋexecutionᚋfederationtestingᚋaccountsᚋgraphᚋmodelᚐCategory(ctx context.Context, sel ast.SelectionSet, v *model.Category) graphql.Marshaler { + if v == nil { + return graphql.Null + } + return ec._Category(ctx, sel, v) +} + func (ec *executionContext) marshalOHistory2githubᚗcomᚋwundergraphᚋgraphqlᚑgoᚑtoolsᚋexecutionᚋfederationtestingᚋaccountsᚋgraphᚋmodelᚐHistory(ctx context.Context, sel ast.SelectionSet, v model.History) graphql.Marshaler { if v == nil { return graphql.Null @@ -8763,6 +9435,20 @@ func (ec *executionContext) marshalOIdentifiable2githubᚗcomᚋwundergraphᚋgr return ec._Identifiable(ctx, sel, v) } +func (ec *executionContext) marshalOOwner2ᚖgithub.comᚋwundergraphᚋgraphqlᚑgoᚑtoolsᚋexecutionᚋfederationtestingᚋaccountsᚋgraphᚋmodelᚐOwner(ctx context.Context, sel ast.SelectionSet, v *model.Owner) graphql.Marshaler { + if v == nil { + return graphql.Null + } + return ec._Owner(ctx, sel, v) +} + +func (ec *executionContext) marshalOProductA2ᚖgithub.comᚋwundergraphᚋgraphqlᚑgoᚑtoolsᚋexecutionᚋfederationtestingᚋaccountsᚋgraphᚋmodelᚐProductA(ctx context.Context, sel ast.SelectionSet, v *model.ProductA) graphql.Marshaler { + if v == nil { + return graphql.Null + } + return ec._ProductA(ctx, sel, v) +} + func (ec *executionContext) marshalOSomeInterface2githubᚗcomᚋwundergraphᚋgraphqlᚑgoᚑtoolsᚋexecutionᚋfederationtestingᚋaccountsᚋgraphᚋmodelᚐSomeInterface(ctx context.Context, sel ast.SelectionSet, v model.SomeInterface) graphql.Marshaler { if v == nil { return graphql.Null diff --git a/execution/federationtesting/accounts/graph/model/models_gen.go b/execution/federationtesting/accounts/graph/model/models_gen.go index 43e756971..f3ce8742d 100644 --- a/execution/federationtesting/accounts/graph/model/models_gen.go +++ b/execution/federationtesting/accounts/graph/model/models_gen.go @@ -18,6 +18,11 @@ type AbstractListItem interface { GetObj() OtherInterface } +type Base interface { + IsBase() + GetCategory() *Category +} + type Cd interface { IsCd() } @@ -120,6 +125,11 @@ type Cat struct { Name string `json:"name"` } +type Category struct { + ID string `json:"id"` + Owner *Owner `json:"owner,omitempty"` +} + type ConcreteListItem1 struct { Obj OtherInterface `json:"obj"` } @@ -143,12 +153,32 @@ func (D) IsCd() {} func (D) IsCDer() {} func (this D) GetName() *CDerObj { return this.Name } +type Owner struct { + Name string `json:"name"` +} + type Product struct { Upc string `json:"upc"` } func (Product) IsEntity() {} +type ProductA struct { + ID string `json:"id"` + Category *Category `json:"category,omitempty"` +} + +func (ProductA) IsBase() {} +func (this ProductA) GetCategory() *Category { return this.Category } + +type ProductB struct { + ID string `json:"id"` + Category *Category `json:"category,omitempty"` +} + +func (ProductB) IsBase() {} +func (this ProductB) GetCategory() *Category { return this.Category } + type Purchase struct { Product *Product `json:"product"` Wallet Wallet `json:"wallet,omitempty"` diff --git a/execution/federationtesting/accounts/graph/schema.graphqls b/execution/federationtesting/accounts/graph/schema.graphqls index 1f8806c71..c59f39512 100644 --- a/execution/federationtesting/accounts/graph/schema.graphqls +++ b/execution/federationtesting/accounts/graph/schema.graphqls @@ -11,6 +11,7 @@ type Query { cds: [CD] otherInterfaces: [SomeInterface] someNestedInterfaces: [SomeNestedInterface] + productA: ProductA } type Cat { @@ -181,4 +182,24 @@ type CDerObj { first: String! middle: String! last: String! -} \ No newline at end of file +} + +interface Base { + category: Category +} +type Category { + id: ID! + owner: Owner +} +type Owner { + name: String! +} + +type ProductA implements Base { + id: ID! + category: Category +} +type ProductB implements Base { + id: ID! + category: Category +} diff --git a/execution/federationtesting/accounts/graph/schema.resolvers.go b/execution/federationtesting/accounts/graph/schema.resolvers.go index 1b56e6475..93d93d0a9 100644 --- a/execution/federationtesting/accounts/graph/schema.resolvers.go +++ b/execution/federationtesting/accounts/graph/schema.resolvers.go @@ -211,6 +211,19 @@ func (r *queryResolver) SomeNestedInterfaces(ctx context.Context) ([]model.SomeN }, nil } +// ProductA is the resolver for the productA field. +func (r *queryResolver) ProductA(ctx context.Context) (*model.ProductA, error) { + return &model.ProductA{ + ID: "p111", + Category: &model.Category{ + ID: "c111", + Owner: &model.Owner{ + Name: "owner", + }, + }, + }, nil +} + // Query returns generated.QueryResolver implementation. func (r *Resolver) Query() generated.QueryResolver { return &queryResolver{r} } diff --git a/execution/federationtesting/testdata/queries/merge_concrete_type_in_root_field_and_interface_fragment.graphql b/execution/federationtesting/testdata/queries/merge_concrete_type_in_root_field_and_interface_fragment.graphql new file mode 100644 index 000000000..68706a0da --- /dev/null +++ b/execution/federationtesting/testdata/queries/merge_concrete_type_in_root_field_and_interface_fragment.graphql @@ -0,0 +1,27 @@ +query MergeConcreteTypeInRootFieldAndInterfaceFragment { + productA { + category { + id + } + ... on Base { + ...ProductDetail + } + } +} + +fragment ProductDetail on Base { + ... on ProductB { + category { + displayOwner: owner { + name + } + } + } + ... on ProductA { + category { + displayOwner: owner { + name + } + } + } +}