From 0dfb6a20f3c9af3866badf3f31aa3ff955e6b62b Mon Sep 17 00:00:00 2001 From: Jens Neuse Date: Wed, 26 Jun 2024 15:57:05 +0200 Subject: [PATCH 1/4] fix: merging fields correctly --- .../federation_integration_static_test.go | 5 +- .../engine/federation_integration_test.go | 38 +- .../accounts/graph/generated/generated.go | 1109 +++++++++++++++-- .../accounts/graph/model/models_gen.go | 75 +- .../accounts/graph/schema.graphqls | 34 +- .../accounts/graph/schema.resolvers.go | 101 ++ .../testdata/queries/abstract_object.graphql | 30 +- .../queries/abstract_object_nested.graphql | 48 + .../abstract_object_nested_reverse.graphql | 48 + .../abstract_object_non_shared.graphql | 19 + .../queries/nested_object_merging.graphql | 17 + v2/pkg/engine/postprocess/merge_fields.go | 166 ++- .../engine/postprocess/merge_fields_test.go | 229 ++++ v2/pkg/engine/resolve/node_object.go | 6 + v2/pkg/engine/resolve/resolvable.go | 49 +- 15 files changed, 1820 insertions(+), 154 deletions(-) create mode 100644 execution/federationtesting/testdata/queries/abstract_object_nested.graphql create mode 100644 execution/federationtesting/testdata/queries/abstract_object_nested_reverse.graphql create mode 100644 execution/federationtesting/testdata/queries/abstract_object_non_shared.graphql create mode 100644 execution/federationtesting/testdata/queries/nested_object_merging.graphql create mode 100644 v2/pkg/engine/postprocess/merge_fields_test.go diff --git a/execution/engine/federation_integration_static_test.go b/execution/engine/federation_integration_static_test.go index a3225a4f7..fa2e71a2d 100644 --- a/execution/engine/federation_integration_static_test.go +++ b/execution/engine/federation_integration_static_test.go @@ -77,7 +77,7 @@ subscription UpdatedPrice { }` gqlRequest := &graphql.Request{ - OperationName: "", + OperationName: "UpdatedPrice", Variables: nil, Query: query, } @@ -96,7 +96,8 @@ subscription UpdatedPrice { }) go func() { - _ = engine.Execute(execCtx, gqlRequest, &resultWriter) + err := engine.Execute(execCtx, gqlRequest, &resultWriter) + require.NoError(t, err) }() assert.Eventuallyf(t, func() bool { diff --git a/execution/engine/federation_integration_test.go b/execution/engine/federation_integration_test.go index 237aaba85..ecfafd841 100644 --- a/execution/engine/federation_integration_test.go +++ b/execution/engine/federation_integration_test.go @@ -466,7 +466,43 @@ func TestFederationIntegrationTest(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) t.Cleanup(cancel) resp := gqlClient.Query(ctx, setup.GatewayServer.URL, testQueryPath("queries/abstract_object.graphql"), nil, t) - expected := `{"data":{"abstractList":[{"__typename":"ConcreteListItem1","obj":{"__typename":"SomeType1","name":"1","age":1}},{"__typename":"ConcreteListItem2","obj":{"__typename":"SomeType2","name":"2","height":2}}]}}` + expected := `{"data":{"otherInterfaces":[{"someObject":{"a":"A","b":"B"}},{"someObject":{"a":"AA","c":"CC"}},{"someObject":{"a":"AAA"}}]}}` + assert.Equal(t, compact(expected), string(resp)) + }) + + t.Run("Abstract object non shared", func(t *testing.T) { + setup := federationtesting.NewFederationSetup(addGateway(false)) + defer setup.Close() + + gqlClient := NewGraphqlClient(http.DefaultClient) + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) + resp := gqlClient.Query(ctx, setup.GatewayServer.URL, testQueryPath("queries/abstract_object_non_shared.graphql"), nil, t) + expected := `{"data":{"otherInterfaces":[{"someObject":{"a":"A"}},{"someObject":{"b":"BB"}},{"someObject":{"c":"CCC"}}]}}` + assert.Equal(t, compact(expected), string(resp)) + }) + + t.Run("Abstract object nested", func(t *testing.T) { + setup := federationtesting.NewFederationSetup(addGateway(false)) + defer setup.Close() + + gqlClient := NewGraphqlClient(http.DefaultClient) + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) + resp := gqlClient.Query(ctx, setup.GatewayServer.URL, testQueryPath("queries/abstract_object_nested.graphql"), nil, t) + expected := `{"data":{"someNestedInterfaces":[{"__typename":"SomeNestedType1","otherInterfaces":[{"__typename":"SomeType1","someObject":{"a":"1.A","c":"1.C","b":"1.B"}},{"__typename":"SomeType2","someObject":{"a":"1.AA","c":"1.CC"}},{"__typename":"SomeType3","someObject":{"a":"1.AAA"}}]},{"__typename":"SomeNestedType2","otherInterfaces":[{"__typename":"SomeType1","someObject":{"a":"2.A","c":"2.C","b":"2.B"}},{"__typename":"SomeType2","someObject":{"a":"2.AA","c":"2.CC"}},{"__typename":"SomeType3","someObject":{"a":"2.AAA"}}]}]}}` + assert.Equal(t, compact(expected), string(resp)) + }) + + t.Run("Abstract object nested reverse", func(t *testing.T) { + setup := federationtesting.NewFederationSetup(addGateway(false)) + defer setup.Close() + + gqlClient := NewGraphqlClient(http.DefaultClient) + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) + resp := gqlClient.Query(ctx, setup.GatewayServer.URL, testQueryPath("queries/abstract_object_nested_reverse.graphql"), nil, t) + expected := `{"data":{"someNestedInterfaces":[{"__typename":"SomeNestedType1","otherInterfaces":[{"someObject":{"a":"1.A","c":"1.C","b":"1.B"},"__typename":"SomeType1"},{"someObject":{"a":"1.AA","c":"1.CC"},"__typename":"SomeType2"},{"someObject":{"a":"1.AAA"},"__typename":"SomeType3"}]},{"__typename":"SomeNestedType2","otherInterfaces":[{"someObject":{"a":"2.A","c":"2.C","b":"2.B"},"__typename":"SomeType1"},{"someObject":{"a":"2.AA","c":"2.CC"},"__typename":"SomeType2"},{"someObject":{"a":"2.AAA"},"__typename":"SomeType3"}]}]}}` assert.Equal(t, compact(expected), string(resp)) }) diff --git a/execution/federationtesting/accounts/graph/generated/generated.go b/execution/federationtesting/accounts/graph/generated/generated.go index 363f4e75f..765ab44e4 100644 --- a/execution/federationtesting/accounts/graph/generated/generated.go +++ b/execution/federationtesting/accounts/graph/generated/generated.go @@ -94,16 +94,18 @@ type ComplexityRoot struct { } Query struct { - AbstractList func(childComplexity int) int - Cat func(childComplexity int) int - Cds func(childComplexity int) int - Histories func(childComplexity int) int - Identifiable func(childComplexity int) int - InterfaceUnion func(childComplexity int, which model.Which) int - Me func(childComplexity int) int - TitleName func(childComplexity int) int - __resolve__service func(childComplexity int) int - __resolve_entities func(childComplexity int, representations []map[string]interface{}) int + AbstractList func(childComplexity int) int + Cat func(childComplexity int) int + Cds func(childComplexity int) int + Histories func(childComplexity int) int + Identifiable func(childComplexity int) int + InterfaceUnion func(childComplexity int, which model.Which) int + Me func(childComplexity int) int + OtherInterfaces func(childComplexity int) int + SomeNestedInterfaces func(childComplexity int) int + TitleName func(childComplexity int) int + __resolve__service func(childComplexity int) int + __resolve_entities func(childComplexity int, representations []map[string]interface{}) int } Sale struct { @@ -112,16 +114,36 @@ type ComplexityRoot struct { Rating func(childComplexity int) int } + SomeNestedType1 struct { + OtherInterfaces func(childComplexity int) int + } + + SomeNestedType2 struct { + OtherInterfaces func(childComplexity int) int + } + + SomeObject struct { + A func(childComplexity int) int + B func(childComplexity int) int + C func(childComplexity int) int + } + SomeType1 struct { - Age func(childComplexity int) int - Name func(childComplexity int) int - Names func(childComplexity int) int + Age func(childComplexity int) int + Name func(childComplexity int) int + Names func(childComplexity int) int + SomeObject func(childComplexity int) int } SomeType2 struct { - Height func(childComplexity int) int - Name func(childComplexity int) int - Names func(childComplexity int) int + Height func(childComplexity int) int + Name func(childComplexity int) int + Names func(childComplexity int) int + SomeObject func(childComplexity int) int + } + + SomeType3 struct { + SomeObject func(childComplexity int) int } TitleName struct { @@ -168,6 +190,8 @@ type QueryResolver interface { AbstractList(ctx context.Context) ([]model.AbstractListItem, error) TitleName(ctx context.Context) (*model.TitleName, error) Cds(ctx context.Context) ([]model.Cd, error) + OtherInterfaces(ctx context.Context) ([]model.SomeInterface, error) + SomeNestedInterfaces(ctx context.Context) ([]model.SomeNestedInterface, error) } type executableSchema struct { @@ -349,6 +373,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Query.Me(childComplexity), true + case "Query.otherInterfaces": + if e.complexity.Query.OtherInterfaces == nil { + break + } + + return e.complexity.Query.OtherInterfaces(childComplexity), true + + case "Query.someNestedInterfaces": + if e.complexity.Query.SomeNestedInterfaces == nil { + break + } + + return e.complexity.Query.SomeNestedInterfaces(childComplexity), true + case "Query.titleName": if e.complexity.Query.TitleName == nil { break @@ -396,6 +434,41 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Sale.Rating(childComplexity), true + case "SomeNestedType1.otherInterfaces": + if e.complexity.SomeNestedType1.OtherInterfaces == nil { + break + } + + return e.complexity.SomeNestedType1.OtherInterfaces(childComplexity), true + + case "SomeNestedType2.otherInterfaces": + if e.complexity.SomeNestedType2.OtherInterfaces == nil { + break + } + + return e.complexity.SomeNestedType2.OtherInterfaces(childComplexity), true + + case "SomeObject.a": + if e.complexity.SomeObject.A == nil { + break + } + + return e.complexity.SomeObject.A(childComplexity), true + + case "SomeObject.b": + if e.complexity.SomeObject.B == nil { + break + } + + return e.complexity.SomeObject.B(childComplexity), true + + case "SomeObject.c": + if e.complexity.SomeObject.C == nil { + break + } + + return e.complexity.SomeObject.C(childComplexity), true + case "SomeType1.age": if e.complexity.SomeType1.Age == nil { break @@ -417,6 +490,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.SomeType1.Names(childComplexity), true + case "SomeType1.someObject": + if e.complexity.SomeType1.SomeObject == nil { + break + } + + return e.complexity.SomeType1.SomeObject(childComplexity), true + case "SomeType2.height": if e.complexity.SomeType2.Height == nil { break @@ -438,6 +518,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.SomeType2.Names(childComplexity), true + case "SomeType2.someObject": + if e.complexity.SomeType2.SomeObject == nil { + break + } + + return e.complexity.SomeType2.SomeObject(childComplexity), true + + case "SomeType3.someObject": + if e.complexity.SomeType3.SomeObject == nil { + break + } + + return e.complexity.SomeType3.SomeObject(childComplexity), true + case "TitleName.a": if e.complexity.TitleName.A == nil { break @@ -613,6 +707,8 @@ var sources = []*ast.Source{ abstractList: [AbstractListItem] titleName: TitleName cds: [CD] + otherInterfaces: [SomeInterface] + someNestedInterfaces: [SomeNestedInterface] } type Cat { @@ -709,16 +805,44 @@ interface OtherInterface { names: [String!]! } -type SomeType1 implements OtherInterface { +interface SomeNestedInterface { + otherInterfaces: [SomeInterface] +} + +type SomeNestedType1 implements SomeNestedInterface { + otherInterfaces: [SomeInterface] +} + +type SomeNestedType2 implements SomeNestedInterface { + otherInterfaces: [SomeInterface] +} + +interface SomeInterface { + someObject: SomeObject! +} + +type SomeType1 implements OtherInterface & SomeInterface { name: String! age: Int! names: [String!]! + someObject: SomeObject! } -type SomeType2 implements OtherInterface { +type SomeType2 implements OtherInterface & SomeInterface { name: String! height: Float! names: [String!]! + someObject: SomeObject! +} + +type SomeType3 implements SomeInterface { + someObject: SomeObject! +} + +type SomeObject { + a: String! + b: String! + c: String! } type TitleName implements Title & Name { @@ -1948,6 +2072,88 @@ func (ec *executionContext) fieldContext_Query_cds(ctx context.Context, field gr return fc, nil } +func (ec *executionContext) _Query_otherInterfaces(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_otherInterfaces(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) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().OtherInterfaces(rctx) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.([]model.SomeInterface) + fc.Result = res + return ec.marshalOSomeInterface2ᚕgithubᚗcomᚋwundergraphᚋgraphqlᚑgoᚑtoolsᚋexecutionᚋfederationtestingᚋaccountsᚋgraphᚋmodelᚐSomeInterface(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_otherInterfaces(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) { + return nil, errors.New("FieldContext.Child cannot be called on type INTERFACE") + }, + } + return fc, nil +} + +func (ec *executionContext) _Query_someNestedInterfaces(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_someNestedInterfaces(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) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().SomeNestedInterfaces(rctx) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.([]model.SomeNestedInterface) + fc.Result = res + return ec.marshalOSomeNestedInterface2ᚕgithubᚗcomᚋwundergraphᚋgraphqlᚑgoᚑtoolsᚋexecutionᚋfederationtestingᚋaccountsᚋgraphᚋmodelᚐSomeNestedInterface(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_someNestedInterfaces(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) { + return nil, errors.New("FieldContext.Child cannot be called on type INTERFACE") + }, + } + 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 { @@ -2316,8 +2522,8 @@ func (ec *executionContext) fieldContext_Sale_location(ctx context.Context, fiel return fc, nil } -func (ec *executionContext) _SomeType1_name(ctx context.Context, field graphql.CollectedField, obj *model.SomeType1) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_SomeType1_name(ctx, field) +func (ec *executionContext) _SomeNestedType1_otherInterfaces(ctx context.Context, field graphql.CollectedField, obj *model.SomeNestedType1) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_SomeNestedType1_otherInterfaces(ctx, field) if err != nil { return graphql.Null } @@ -2330,38 +2536,35 @@ func (ec *executionContext) _SomeType1_name(ctx context.Context, field graphql.C }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Name, nil + return obj.OtherInterfaces, 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.SomeInterface) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalOSomeInterface2ᚕgithubᚗcomᚋwundergraphᚋgraphqlᚑgoᚑtoolsᚋexecutionᚋfederationtestingᚋaccountsᚋgraphᚋmodelᚐSomeInterface(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_SomeType1_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_SomeNestedType1_otherInterfaces(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "SomeType1", + Object: "SomeNestedType1", 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 nil, errors.New("FieldContext.Child cannot be called on type INTERFACE") }, } return fc, nil } -func (ec *executionContext) _SomeType1_age(ctx context.Context, field graphql.CollectedField, obj *model.SomeType1) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_SomeType1_age(ctx, field) +func (ec *executionContext) _SomeNestedType2_otherInterfaces(ctx context.Context, field graphql.CollectedField, obj *model.SomeNestedType2) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_SomeNestedType2_otherInterfaces(ctx, field) if err != nil { return graphql.Null } @@ -2374,38 +2577,35 @@ func (ec *executionContext) _SomeType1_age(ctx context.Context, field graphql.Co }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Age, nil + return obj.OtherInterfaces, 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.(int) + res := resTmp.([]model.SomeInterface) fc.Result = res - return ec.marshalNInt2int(ctx, field.Selections, res) + return ec.marshalOSomeInterface2ᚕgithubᚗcomᚋwundergraphᚋgraphqlᚑgoᚑtoolsᚋexecutionᚋfederationtestingᚋaccountsᚋgraphᚋmodelᚐSomeInterface(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_SomeType1_age(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_SomeNestedType2_otherInterfaces(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "SomeType1", + Object: "SomeNestedType2", 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 nil, errors.New("FieldContext.Child cannot be called on type INTERFACE") }, } return fc, nil } -func (ec *executionContext) _SomeType1_names(ctx context.Context, field graphql.CollectedField, obj *model.SomeType1) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_SomeType1_names(ctx, field) +func (ec *executionContext) _SomeObject_a(ctx context.Context, field graphql.CollectedField, obj *model.SomeObject) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_SomeObject_a(ctx, field) if err != nil { return graphql.Null } @@ -2418,7 +2618,7 @@ func (ec *executionContext) _SomeType1_names(ctx context.Context, field graphql. }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Names, nil + return obj.A, nil }) if err != nil { ec.Error(ctx, err) @@ -2430,14 +2630,14 @@ func (ec *executionContext) _SomeType1_names(ctx context.Context, field graphql. } return graphql.Null } - res := resTmp.([]string) + res := resTmp.(string) fc.Result = res - return ec.marshalNString2ᚕstringᚄ(ctx, field.Selections, res) + return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_SomeType1_names(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_SomeObject_a(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "SomeType1", + Object: "SomeObject", Field: field, IsMethod: false, IsResolver: false, @@ -2448,8 +2648,8 @@ func (ec *executionContext) fieldContext_SomeType1_names(ctx context.Context, fi return fc, nil } -func (ec *executionContext) _SomeType2_name(ctx context.Context, field graphql.CollectedField, obj *model.SomeType2) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_SomeType2_name(ctx, field) +func (ec *executionContext) _SomeObject_b(ctx context.Context, field graphql.CollectedField, obj *model.SomeObject) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_SomeObject_b(ctx, field) if err != nil { return graphql.Null } @@ -2462,7 +2662,7 @@ func (ec *executionContext) _SomeType2_name(ctx context.Context, field graphql.C }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Name, nil + return obj.B, nil }) if err != nil { ec.Error(ctx, err) @@ -2479,9 +2679,9 @@ func (ec *executionContext) _SomeType2_name(ctx context.Context, field graphql.C return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_SomeType2_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_SomeObject_b(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "SomeType2", + Object: "SomeObject", Field: field, IsMethod: false, IsResolver: false, @@ -2492,8 +2692,8 @@ func (ec *executionContext) fieldContext_SomeType2_name(ctx context.Context, fie return fc, nil } -func (ec *executionContext) _SomeType2_height(ctx context.Context, field graphql.CollectedField, obj *model.SomeType2) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_SomeType2_height(ctx, field) +func (ec *executionContext) _SomeObject_c(ctx context.Context, field graphql.CollectedField, obj *model.SomeObject) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_SomeObject_c(ctx, field) if err != nil { return graphql.Null } @@ -2506,7 +2706,7 @@ func (ec *executionContext) _SomeType2_height(ctx context.Context, field graphql }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Height, nil + return obj.C, nil }) if err != nil { ec.Error(ctx, err) @@ -2518,26 +2718,26 @@ func (ec *executionContext) _SomeType2_height(ctx context.Context, field graphql } return graphql.Null } - res := resTmp.(float64) + res := resTmp.(string) fc.Result = res - return ec.marshalNFloat2float64(ctx, field.Selections, res) + return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_SomeType2_height(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_SomeObject_c(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "SomeType2", + Object: "SomeObject", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type Float does not have child fields") + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) _SomeType2_names(ctx context.Context, field graphql.CollectedField, obj *model.SomeType2) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_SomeType2_names(ctx, field) +func (ec *executionContext) _SomeType1_name(ctx context.Context, field graphql.CollectedField, obj *model.SomeType1) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_SomeType1_name(ctx, field) if err != nil { return graphql.Null } @@ -2550,7 +2750,7 @@ func (ec *executionContext) _SomeType2_names(ctx context.Context, field graphql. }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Names, nil + return obj.Name, nil }) if err != nil { ec.Error(ctx, err) @@ -2562,14 +2762,14 @@ func (ec *executionContext) _SomeType2_names(ctx context.Context, field graphql. } return graphql.Null } - res := resTmp.([]string) + res := resTmp.(string) fc.Result = res - return ec.marshalNString2ᚕstringᚄ(ctx, field.Selections, res) + return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_SomeType2_names(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_SomeType1_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "SomeType2", + Object: "SomeType1", Field: field, IsMethod: false, IsResolver: false, @@ -2580,8 +2780,8 @@ func (ec *executionContext) fieldContext_SomeType2_names(ctx context.Context, fi return fc, nil } -func (ec *executionContext) _TitleName_a(ctx context.Context, field graphql.CollectedField, obj *model.TitleName) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TitleName_a(ctx, field) +func (ec *executionContext) _SomeType1_age(ctx context.Context, field graphql.CollectedField, obj *model.SomeType1) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_SomeType1_age(ctx, field) if err != nil { return graphql.Null } @@ -2594,7 +2794,7 @@ func (ec *executionContext) _TitleName_a(ctx context.Context, field graphql.Coll }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.A, nil + return obj.Age, nil }) if err != nil { ec.Error(ctx, err) @@ -2606,26 +2806,26 @@ func (ec *executionContext) _TitleName_a(ctx context.Context, field graphql.Coll } return graphql.Null } - res := resTmp.(string) + res := resTmp.(int) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalNInt2int(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TitleName_a(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_SomeType1_age(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TitleName", + Object: "SomeType1", 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 nil, errors.New("field of type Int does not have child fields") }, } return fc, nil } -func (ec *executionContext) _TitleName_b(ctx context.Context, field graphql.CollectedField, obj *model.TitleName) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TitleName_b(ctx, field) +func (ec *executionContext) _SomeType1_names(ctx context.Context, field graphql.CollectedField, obj *model.SomeType1) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_SomeType1_names(ctx, field) if err != nil { return graphql.Null } @@ -2638,7 +2838,7 @@ func (ec *executionContext) _TitleName_b(ctx context.Context, field graphql.Coll }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.B, nil + return obj.Names, nil }) if err != nil { ec.Error(ctx, err) @@ -2650,14 +2850,14 @@ func (ec *executionContext) _TitleName_b(ctx context.Context, field graphql.Coll } return graphql.Null } - res := resTmp.(string) + res := resTmp.([]string) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalNString2ᚕstringᚄ(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TitleName_b(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_SomeType1_names(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TitleName", + Object: "SomeType1", Field: field, IsMethod: false, IsResolver: false, @@ -2668,8 +2868,8 @@ func (ec *executionContext) fieldContext_TitleName_b(ctx context.Context, field return fc, nil } -func (ec *executionContext) _TitleName_c(ctx context.Context, field graphql.CollectedField, obj *model.TitleName) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TitleName_c(ctx, field) +func (ec *executionContext) _SomeType1_someObject(ctx context.Context, field graphql.CollectedField, obj *model.SomeType1) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_SomeType1_someObject(ctx, field) if err != nil { return graphql.Null } @@ -2682,7 +2882,7 @@ func (ec *executionContext) _TitleName_c(ctx context.Context, field graphql.Coll }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.C, nil + return obj.SomeObject, nil }) if err != nil { ec.Error(ctx, err) @@ -2694,26 +2894,34 @@ func (ec *executionContext) _TitleName_c(ctx context.Context, field graphql.Coll } return graphql.Null } - res := resTmp.(string) + res := resTmp.(*model.SomeObject) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalNSomeObject2ᚖgithubᚗcomᚋwundergraphᚋgraphqlᚑgoᚑtoolsᚋexecutionᚋfederationtestingᚋaccountsᚋgraphᚋmodelᚐSomeObject(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TitleName_c(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_SomeType1_someObject(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TitleName", + Object: "SomeType1", 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 "a": + return ec.fieldContext_SomeObject_a(ctx, field) + case "b": + return ec.fieldContext_SomeObject_b(ctx, field) + case "c": + return ec.fieldContext_SomeObject_c(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type SomeObject", field.Name) }, } return fc, nil } -func (ec *executionContext) _TitleName_title(ctx context.Context, field graphql.CollectedField, obj *model.TitleName) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TitleName_title(ctx, field) +func (ec *executionContext) _SomeType2_name(ctx context.Context, field graphql.CollectedField, obj *model.SomeType2) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_SomeType2_name(ctx, field) if err != nil { return graphql.Null } @@ -2726,7 +2934,7 @@ func (ec *executionContext) _TitleName_title(ctx context.Context, field graphql. }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Title, nil + return obj.Name, nil }) if err != nil { ec.Error(ctx, err) @@ -2743,9 +2951,9 @@ func (ec *executionContext) _TitleName_title(ctx context.Context, field graphql. return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TitleName_title(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_SomeType2_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TitleName", + Object: "SomeType2", Field: field, IsMethod: false, IsResolver: false, @@ -2756,8 +2964,376 @@ func (ec *executionContext) fieldContext_TitleName_title(ctx context.Context, fi return fc, nil } -func (ec *executionContext) _TitleName_name(ctx context.Context, field graphql.CollectedField, obj *model.TitleName) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TitleName_name(ctx, field) +func (ec *executionContext) _SomeType2_height(ctx context.Context, field graphql.CollectedField, obj *model.SomeType2) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_SomeType2_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, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Height, 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.(float64) + fc.Result = res + return ec.marshalNFloat2float64(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_SomeType2_height(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "SomeType2", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Float does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _SomeType2_names(ctx context.Context, field graphql.CollectedField, obj *model.SomeType2) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_SomeType2_names(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) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Names, 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.marshalNString2ᚕstringᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_SomeType2_names(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "SomeType2", + 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) _SomeType2_someObject(ctx context.Context, field graphql.CollectedField, obj *model.SomeType2) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_SomeType2_someObject(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) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.SomeObject, 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.(*model.SomeObject) + fc.Result = res + return ec.marshalNSomeObject2ᚖgithubᚗcomᚋwundergraphᚋgraphqlᚑgoᚑtoolsᚋexecutionᚋfederationtestingᚋaccountsᚋgraphᚋmodelᚐSomeObject(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_SomeType2_someObject(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "SomeType2", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "a": + return ec.fieldContext_SomeObject_a(ctx, field) + case "b": + return ec.fieldContext_SomeObject_b(ctx, field) + case "c": + return ec.fieldContext_SomeObject_c(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type SomeObject", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _SomeType3_someObject(ctx context.Context, field graphql.CollectedField, obj *model.SomeType3) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_SomeType3_someObject(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) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.SomeObject, 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.(*model.SomeObject) + fc.Result = res + return ec.marshalNSomeObject2ᚖgithubᚗcomᚋwundergraphᚋgraphqlᚑgoᚑtoolsᚋexecutionᚋfederationtestingᚋaccountsᚋgraphᚋmodelᚐSomeObject(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_SomeType3_someObject(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "SomeType3", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "a": + return ec.fieldContext_SomeObject_a(ctx, field) + case "b": + return ec.fieldContext_SomeObject_b(ctx, field) + case "c": + return ec.fieldContext_SomeObject_c(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type SomeObject", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _TitleName_a(ctx context.Context, field graphql.CollectedField, obj *model.TitleName) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TitleName_a(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) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.A, 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_TitleName_a(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TitleName", + 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) _TitleName_b(ctx context.Context, field graphql.CollectedField, obj *model.TitleName) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TitleName_b(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) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.B, 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_TitleName_b(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TitleName", + 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) _TitleName_c(ctx context.Context, field graphql.CollectedField, obj *model.TitleName) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TitleName_c(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) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.C, 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_TitleName_c(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TitleName", + 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) _TitleName_title(ctx context.Context, field graphql.CollectedField, obj *model.TitleName) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TitleName_title(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) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Title, 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_TitleName_title(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TitleName", + 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) _TitleName_name(ctx context.Context, field graphql.CollectedField, obj *model.TitleName) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TitleName_name(ctx, field) if err != nil { return graphql.Null } @@ -5244,7 +5820,30 @@ func (ec *executionContext) _Namer(ctx context.Context, sel ast.SelectionSet, ob } } -func (ec *executionContext) _OtherInterface(ctx context.Context, sel ast.SelectionSet, obj model.OtherInterface) graphql.Marshaler { +func (ec *executionContext) _OtherInterface(ctx context.Context, sel ast.SelectionSet, obj model.OtherInterface) graphql.Marshaler { + switch obj := (obj).(type) { + case nil: + return graphql.Null + case model.SomeType1: + return ec._SomeType1(ctx, sel, &obj) + case *model.SomeType1: + if obj == nil { + return graphql.Null + } + return ec._SomeType1(ctx, sel, obj) + case model.SomeType2: + return ec._SomeType2(ctx, sel, &obj) + case *model.SomeType2: + if obj == nil { + return graphql.Null + } + return ec._SomeType2(ctx, sel, obj) + default: + panic(fmt.Errorf("unexpected type %T", obj)) + } +} + +func (ec *executionContext) _SomeInterface(ctx context.Context, sel ast.SelectionSet, obj model.SomeInterface) graphql.Marshaler { switch obj := (obj).(type) { case nil: return graphql.Null @@ -5262,6 +5861,36 @@ func (ec *executionContext) _OtherInterface(ctx context.Context, sel ast.Selecti return graphql.Null } return ec._SomeType2(ctx, sel, obj) + case model.SomeType3: + return ec._SomeType3(ctx, sel, &obj) + case *model.SomeType3: + if obj == nil { + return graphql.Null + } + return ec._SomeType3(ctx, sel, obj) + default: + panic(fmt.Errorf("unexpected type %T", obj)) + } +} + +func (ec *executionContext) _SomeNestedInterface(ctx context.Context, sel ast.SelectionSet, obj model.SomeNestedInterface) graphql.Marshaler { + switch obj := (obj).(type) { + case nil: + return graphql.Null + case model.SomeNestedType1: + return ec._SomeNestedType1(ctx, sel, &obj) + case *model.SomeNestedType1: + if obj == nil { + return graphql.Null + } + return ec._SomeNestedType1(ctx, sel, obj) + case model.SomeNestedType2: + return ec._SomeNestedType2(ctx, sel, &obj) + case *model.SomeNestedType2: + if obj == nil { + return graphql.Null + } + return ec._SomeNestedType2(ctx, sel, obj) default: panic(fmt.Errorf("unexpected type %T", obj)) } @@ -5877,6 +6506,46 @@ 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 "otherInterfaces": + 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_otherInterfaces(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) + }) + case "someNestedInterfaces": + 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_someNestedInterfaces(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) }) @@ -5991,7 +6660,99 @@ func (ec *executionContext) _Sale(ctx context.Context, sel ast.SelectionSet, obj return out } -var someType1Implementors = []string{"SomeType1", "OtherInterface"} +var someNestedType1Implementors = []string{"SomeNestedType1", "SomeNestedInterface"} + +func (ec *executionContext) _SomeNestedType1(ctx context.Context, sel ast.SelectionSet, obj *model.SomeNestedType1) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, someNestedType1Implementors) + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("SomeNestedType1") + case "otherInterfaces": + + out.Values[i] = ec._SomeNestedType1_otherInterfaces(ctx, field, obj) + + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalids > 0 { + return graphql.Null + } + return out +} + +var someNestedType2Implementors = []string{"SomeNestedType2", "SomeNestedInterface"} + +func (ec *executionContext) _SomeNestedType2(ctx context.Context, sel ast.SelectionSet, obj *model.SomeNestedType2) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, someNestedType2Implementors) + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("SomeNestedType2") + case "otherInterfaces": + + out.Values[i] = ec._SomeNestedType2_otherInterfaces(ctx, field, obj) + + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalids > 0 { + return graphql.Null + } + return out +} + +var someObjectImplementors = []string{"SomeObject"} + +func (ec *executionContext) _SomeObject(ctx context.Context, sel ast.SelectionSet, obj *model.SomeObject) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, someObjectImplementors) + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("SomeObject") + case "a": + + out.Values[i] = ec._SomeObject_a(ctx, field, obj) + + if out.Values[i] == graphql.Null { + invalids++ + } + case "b": + + out.Values[i] = ec._SomeObject_b(ctx, field, obj) + + if out.Values[i] == graphql.Null { + invalids++ + } + case "c": + + out.Values[i] = ec._SomeObject_c(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 someType1Implementors = []string{"SomeType1", "OtherInterface", "SomeInterface"} func (ec *executionContext) _SomeType1(ctx context.Context, sel ast.SelectionSet, obj *model.SomeType1) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, someType1Implementors) @@ -6019,6 +6780,13 @@ func (ec *executionContext) _SomeType1(ctx context.Context, sel ast.SelectionSet out.Values[i] = ec._SomeType1_names(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } + case "someObject": + + out.Values[i] = ec._SomeType1_someObject(ctx, field, obj) + if out.Values[i] == graphql.Null { invalids++ } @@ -6033,7 +6801,7 @@ func (ec *executionContext) _SomeType1(ctx context.Context, sel ast.SelectionSet return out } -var someType2Implementors = []string{"SomeType2", "OtherInterface"} +var someType2Implementors = []string{"SomeType2", "OtherInterface", "SomeInterface"} func (ec *executionContext) _SomeType2(ctx context.Context, sel ast.SelectionSet, obj *model.SomeType2) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, someType2Implementors) @@ -6061,6 +6829,41 @@ func (ec *executionContext) _SomeType2(ctx context.Context, sel ast.SelectionSet out.Values[i] = ec._SomeType2_names(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } + case "someObject": + + out.Values[i] = ec._SomeType2_someObject(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 someType3Implementors = []string{"SomeType3", "SomeInterface"} + +func (ec *executionContext) _SomeType3(ctx context.Context, sel ast.SelectionSet, obj *model.SomeType3) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, someType3Implementors) + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("SomeType3") + case "someObject": + + out.Values[i] = ec._SomeType3_someObject(ctx, field, obj) + if out.Values[i] == graphql.Null { invalids++ } @@ -6741,6 +7544,16 @@ func (ec *executionContext) marshalNProduct2ᚖgithubᚗcomᚋwundergraphᚋgrap return ec._Product(ctx, sel, v) } +func (ec *executionContext) marshalNSomeObject2ᚖgithubᚗcomᚋwundergraphᚋgraphqlᚑgoᚑtoolsᚋexecutionᚋfederationtestingᚋaccountsᚋgraphᚋmodelᚐSomeObject(ctx context.Context, sel ast.SelectionSet, v *model.SomeObject) 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._SomeObject(ctx, sel, v) +} + func (ec *executionContext) unmarshalNString2string(ctx context.Context, v interface{}) (string, error) { res, err := graphql.UnmarshalString(v) return res, graphql.ErrorOnPath(ctx, err) @@ -7373,6 +8186,102 @@ func (ec *executionContext) marshalOIdentifiable2githubᚗcomᚋwundergraphᚋgr return ec._Identifiable(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 + } + return ec._SomeInterface(ctx, sel, v) +} + +func (ec *executionContext) marshalOSomeInterface2ᚕgithubᚗ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 + } + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalOSomeInterface2githubᚗcomᚋwundergraphᚋgraphqlᚑgoᚑtoolsᚋexecutionᚋfederationtestingᚋaccountsᚋgraphᚋmodelᚐSomeInterface(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + return ret +} + +func (ec *executionContext) marshalOSomeNestedInterface2githubᚗcomᚋwundergraphᚋgraphqlᚑgoᚑtoolsᚋexecutionᚋfederationtestingᚋaccountsᚋgraphᚋmodelᚐSomeNestedInterface(ctx context.Context, sel ast.SelectionSet, v model.SomeNestedInterface) graphql.Marshaler { + if v == nil { + return graphql.Null + } + return ec._SomeNestedInterface(ctx, sel, v) +} + +func (ec *executionContext) marshalOSomeNestedInterface2ᚕgithubᚗcomᚋwundergraphᚋgraphqlᚑgoᚑtoolsᚋexecutionᚋfederationtestingᚋaccountsᚋgraphᚋmodelᚐSomeNestedInterface(ctx context.Context, sel ast.SelectionSet, v []model.SomeNestedInterface) graphql.Marshaler { + if v == nil { + return graphql.Null + } + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalOSomeNestedInterface2githubᚗcomᚋwundergraphᚋgraphqlᚑgoᚑtoolsᚋexecutionᚋfederationtestingᚋaccountsᚋgraphᚋmodelᚐSomeNestedInterface(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + return ret +} + func (ec *executionContext) unmarshalOString2string(ctx context.Context, v interface{}) (string, error) { res, err := graphql.UnmarshalString(v) return res, graphql.ErrorOnPath(ctx, err) diff --git a/execution/federationtesting/accounts/graph/model/models_gen.go b/execution/federationtesting/accounts/graph/model/models_gen.go index c09bb8080..701322698 100644 --- a/execution/federationtesting/accounts/graph/model/models_gen.go +++ b/execution/federationtesting/accounts/graph/model/models_gen.go @@ -56,6 +56,16 @@ type OtherInterface interface { GetNames() []string } +type SomeInterface interface { + IsSomeInterface() + GetSomeObject() *SomeObject +} + +type SomeNestedInterface interface { + IsSomeNestedInterface() + GetOtherInterfaces() []SomeInterface +} + type Store interface { IsStore() GetLocation() string @@ -160,10 +170,49 @@ func (Sale) IsHistory() {} func (Sale) IsStore() {} func (this Sale) GetLocation() string { return this.Location } +type SomeNestedType1 struct { + OtherInterfaces []SomeInterface `json:"otherInterfaces"` +} + +func (SomeNestedType1) IsSomeNestedInterface() {} +func (this SomeNestedType1) GetOtherInterfaces() []SomeInterface { + if this.OtherInterfaces == nil { + return nil + } + interfaceSlice := make([]SomeInterface, 0, len(this.OtherInterfaces)) + for _, concrete := range this.OtherInterfaces { + interfaceSlice = append(interfaceSlice, concrete) + } + return interfaceSlice +} + +type SomeNestedType2 struct { + OtherInterfaces []SomeInterface `json:"otherInterfaces"` +} + +func (SomeNestedType2) IsSomeNestedInterface() {} +func (this SomeNestedType2) GetOtherInterfaces() []SomeInterface { + if this.OtherInterfaces == nil { + return nil + } + interfaceSlice := make([]SomeInterface, 0, len(this.OtherInterfaces)) + for _, concrete := range this.OtherInterfaces { + interfaceSlice = append(interfaceSlice, concrete) + } + return interfaceSlice +} + +type SomeObject struct { + A string `json:"a"` + B string `json:"b"` + C string `json:"c"` +} + type SomeType1 struct { - Name string `json:"name"` - Age int `json:"age"` - Names []string `json:"names"` + Name string `json:"name"` + Age int `json:"age"` + Names []string `json:"names"` + SomeObject *SomeObject `json:"someObject"` } func (SomeType1) IsOtherInterface() {} @@ -179,10 +228,14 @@ func (this SomeType1) GetNames() []string { return interfaceSlice } +func (SomeType1) IsSomeInterface() {} +func (this SomeType1) GetSomeObject() *SomeObject { return this.SomeObject } + type SomeType2 struct { - Name string `json:"name"` - Height float64 `json:"height"` - Names []string `json:"names"` + Name string `json:"name"` + Height float64 `json:"height"` + Names []string `json:"names"` + SomeObject *SomeObject `json:"someObject"` } func (SomeType2) IsOtherInterface() {} @@ -198,6 +251,16 @@ func (this SomeType2) GetNames() []string { return interfaceSlice } +func (SomeType2) IsSomeInterface() {} +func (this SomeType2) GetSomeObject() *SomeObject { return this.SomeObject } + +type SomeType3 struct { + SomeObject *SomeObject `json:"someObject"` +} + +func (SomeType3) IsSomeInterface() {} +func (this SomeType3) GetSomeObject() *SomeObject { return this.SomeObject } + type TitleName struct { A string `json:"a"` B string `json:"b"` diff --git a/execution/federationtesting/accounts/graph/schema.graphqls b/execution/federationtesting/accounts/graph/schema.graphqls index dab265604..1f8806c71 100644 --- a/execution/federationtesting/accounts/graph/schema.graphqls +++ b/execution/federationtesting/accounts/graph/schema.graphqls @@ -9,6 +9,8 @@ type Query { abstractList: [AbstractListItem] titleName: TitleName cds: [CD] + otherInterfaces: [SomeInterface] + someNestedInterfaces: [SomeNestedInterface] } type Cat { @@ -105,16 +107,44 @@ interface OtherInterface { names: [String!]! } -type SomeType1 implements OtherInterface { +interface SomeNestedInterface { + otherInterfaces: [SomeInterface] +} + +type SomeNestedType1 implements SomeNestedInterface { + otherInterfaces: [SomeInterface] +} + +type SomeNestedType2 implements SomeNestedInterface { + otherInterfaces: [SomeInterface] +} + +interface SomeInterface { + someObject: SomeObject! +} + +type SomeType1 implements OtherInterface & SomeInterface { name: String! age: Int! names: [String!]! + someObject: SomeObject! } -type SomeType2 implements OtherInterface { +type SomeType2 implements OtherInterface & SomeInterface { name: String! height: Float! names: [String!]! + someObject: SomeObject! +} + +type SomeType3 implements SomeInterface { + someObject: SomeObject! +} + +type SomeObject { + a: String! + b: String! + c: String! } type TitleName implements Title & Name { diff --git a/execution/federationtesting/accounts/graph/schema.resolvers.go b/execution/federationtesting/accounts/graph/schema.resolvers.go index c6b191aa3..5016550e3 100644 --- a/execution/federationtesting/accounts/graph/schema.resolvers.go +++ b/execution/federationtesting/accounts/graph/schema.resolvers.go @@ -110,6 +110,107 @@ func (r *queryResolver) Cds(ctx context.Context) ([]model.Cd, error) { }, nil } +// OtherInterfaces is the resolver for the otherInterfaces field. +func (r *queryResolver) OtherInterfaces(ctx context.Context) ([]model.SomeInterface, error) { + return []model.SomeInterface{ + &model.SomeType1{ + Name: "1", + Age: 1, + Names: []string{"1", "2"}, + SomeObject: &model.SomeObject{ + A: "A", + B: "B", + C: "C", + }, + }, + &model.SomeType2{ + Name: "2", + Height: 2.0, + Names: []string{"3", "4"}, + SomeObject: &model.SomeObject{ + A: "AA", + B: "BB", + C: "CC", + }, + }, + &model.SomeType3{ + SomeObject: &model.SomeObject{ + A: "AAA", + B: "BBB", + C: "CCC", + }, + }, + }, nil +} + +// SomeNestedInterfaces is the resolver for the someNestedInterfaces field. +func (r *queryResolver) SomeNestedInterfaces(ctx context.Context) ([]model.SomeNestedInterface, error) { + return []model.SomeNestedInterface{ + &model.SomeNestedType1{ + OtherInterfaces: []model.SomeInterface{ + &model.SomeType1{ + Name: "1.1", + Age: 1, + Names: []string{"1.1", "1.2"}, + SomeObject: &model.SomeObject{ + A: "1.A", + B: "1.B", + C: "1.C", + }, + }, + &model.SomeType2{ + Name: "1.2", + Height: 2.0, + Names: []string{"1.3", "1.4"}, + SomeObject: &model.SomeObject{ + A: "1.AA", + B: "1.BB", + C: "1.CC", + }, + }, + &model.SomeType3{ + SomeObject: &model.SomeObject{ + A: "1.AAA", + B: "1.BBB", + C: "1.CCC", + }, + }, + }, + }, + &model.SomeNestedType2{ + OtherInterfaces: []model.SomeInterface{ + &model.SomeType1{ + Name: "2.1", + Age: 1, + Names: []string{"2.1", "2.2"}, + SomeObject: &model.SomeObject{ + A: "2.A", + B: "2.B", + C: "2.C", + }, + }, + &model.SomeType2{ + Name: "2.2", + Height: 2.0, + Names: []string{"2.3", "2.4"}, + SomeObject: &model.SomeObject{ + A: "2.AA", + B: "2.BB", + C: "2.CC", + }, + }, + &model.SomeType3{ + SomeObject: &model.SomeObject{ + A: "2.AAA", + B: "2.BBB", + C: "2.CCC", + }, + }, + }, + }, + }, nil +} + // Query returns generated.QueryResolver implementation. func (r *Resolver) Query() generated.QueryResolver { return &queryResolver{r} } diff --git a/execution/federationtesting/testdata/queries/abstract_object.graphql b/execution/federationtesting/testdata/queries/abstract_object.graphql index 514850483..e7ccc620c 100644 --- a/execution/federationtesting/testdata/queries/abstract_object.graphql +++ b/execution/federationtesting/testdata/queries/abstract_object.graphql @@ -1,28 +1,16 @@ query AbstractObject { - abstractList { - __typename - obj { - __typename - name + otherInterfaces { + someObject { + a } - ... on ConcreteListItem1 { - obj { - __typename - name - ... on SomeType1 { - name - age - } + ... on SomeType1 { + someObject { + b } } - ... on ConcreteListItem2 { - obj { - __typename - name - ... on SomeType2 { - name - height - } + ... on SomeType2 { + someObject { + c } } } diff --git a/execution/federationtesting/testdata/queries/abstract_object_nested.graphql b/execution/federationtesting/testdata/queries/abstract_object_nested.graphql new file mode 100644 index 000000000..a328a23f4 --- /dev/null +++ b/execution/federationtesting/testdata/queries/abstract_object_nested.graphql @@ -0,0 +1,48 @@ +query AbstractObject { + someNestedInterfaces { + __typename + otherInterfaces { + __typename + someObject { + a + } + } + ... on SomeNestedType1 { + otherInterfaces { + someObject { + a + } + ... on SomeType1 { + someObject { + c + b + a + } + } + ... on SomeType2 { + someObject { + c + } + } + } + } + ... on SomeNestedType2 { + otherInterfaces { + __typename + someObject { + c + } + ... on SomeType1 { + someObject { + b + } + } + ... on SomeType2 { + someObject { + a + } + } + } + } + } +} \ No newline at end of file diff --git a/execution/federationtesting/testdata/queries/abstract_object_nested_reverse.graphql b/execution/federationtesting/testdata/queries/abstract_object_nested_reverse.graphql new file mode 100644 index 000000000..52a9a100d --- /dev/null +++ b/execution/federationtesting/testdata/queries/abstract_object_nested_reverse.graphql @@ -0,0 +1,48 @@ +query AbstractObject { + someNestedInterfaces { + __typename + ... on SomeNestedType1 { + otherInterfaces { + __typename + someObject { + a + } + ... on SomeType1 { + someObject { + c + b + a + } + } + ... on SomeType2 { + someObject { + c + } + } + } + } + ... on SomeNestedType2 { + otherInterfaces { + __typename + someObject { + c + } + ... on SomeType1 { + someObject { + b + } + } + ... on SomeType2 { + someObject { + a + } + } + } + } + otherInterfaces { + someObject { + a + } + } + } +} \ No newline at end of file diff --git a/execution/federationtesting/testdata/queries/abstract_object_non_shared.graphql b/execution/federationtesting/testdata/queries/abstract_object_non_shared.graphql new file mode 100644 index 000000000..e2e8e7c59 --- /dev/null +++ b/execution/federationtesting/testdata/queries/abstract_object_non_shared.graphql @@ -0,0 +1,19 @@ +query AbstractObject { + otherInterfaces { + ... on SomeType1 { + someObject { + a + } + } + ... on SomeType2 { + someObject { + b + } + } + ... on SomeType3 { + someObject { + c + } + } + } +} \ No newline at end of file diff --git a/execution/federationtesting/testdata/queries/nested_object_merging.graphql b/execution/federationtesting/testdata/queries/nested_object_merging.graphql new file mode 100644 index 000000000..1a3f17cf5 --- /dev/null +++ b/execution/federationtesting/testdata/queries/nested_object_merging.graphql @@ -0,0 +1,17 @@ +query NestedObjectMerging { + interfaceField { + object { + a + } + ... on A { + object { + b + } + } + ... on B { + object { + c + } + } + } +} \ No newline at end of file diff --git a/v2/pkg/engine/postprocess/merge_fields.go b/v2/pkg/engine/postprocess/merge_fields.go index 2fa69e83b..eca4085f0 100644 --- a/v2/pkg/engine/postprocess/merge_fields.go +++ b/v2/pkg/engine/postprocess/merge_fields.go @@ -41,13 +41,18 @@ func (m *MergeFields) traverseNode(node resolve.Node) { n.Fields = append(n.Fields[:i+1], append([]*resolve.Field{additionalField}, n.Fields[i+1:]...)...) } } + // 2. + // Propagate onTypeNames to all children + // In a later stage, we merge all nested scalar fields with the same name + // However, scalar fields can originate from different parent types, which is why we need to propagate them here + m.propagateParentTypeNames(n.Fields[i]) } - // 2. merge fields without onTypeNames "over" fields with onTypeNames + // 3. merge fields without onTypeNames "over" fields with onTypeNames // This is possible because if a field exists without onTypeNames, it will always be resolved // There are 2 variants of this: - // 2.1. if the source and target fields are scalars, the target (with onTypeNames) will be removed + // 3.1. if the source and target fields are scalars, the target (with onTypeNames) will be removed // This means that scalars with no onTypeNames will overwrite scalars with onTypeNames - // 2.2. if the source and target fields are objects, all fields from the source will be merged into the target + // 3.2. if the source and target fields are objects, all fields from the source will be merged into the target // This means that objects with no onTypeNames will be merged into objects with onTypeNames for i := 0; i < len(n.Fields); i++ { if n.Fields[i].OnTypeNames != nil { @@ -61,22 +66,49 @@ func (m *MergeFields) traverseNode(node resolve.Node) { continue } if bytes.Equal(n.Fields[i].Name, n.Fields[j].Name) { - m.mergeValues(n.Fields[i].Value, n.Fields[j].Value) + m.mergeValues(n.Fields[i], n.Fields[j]) n.Fields = append(n.Fields[:j], n.Fields[j+1:]...) + if i > j { + i-- + } j-- } } } - // 3. merge sibling object fields + // 4. merge sibling object fields for i := 0; i < len(n.Fields); i++ { for j := i + 1; j < len(n.Fields); j++ { if m.fieldsCanMerge(n.Fields[i], n.Fields[j]) { - m.mergeValues(n.Fields[i].Value, n.Fields[j].Value) + m.mergeValues(n.Fields[i], n.Fields[j]) n.Fields = append(n.Fields[:j], n.Fields[j+1:]...) j-- } } } + // 5. merge sibling scalar fields + // Once all objects have been merged, we need to merge (deduplicate) all scalar fields that are left + for i := 0; i < len(n.Fields); i++ { + // skip objects + if m.nodeIsScalar(n.Fields[i].Value) { + for j := 0; j < len(n.Fields); j++ { + if i == j { + continue + } + if bytes.Equal(n.Fields[i].Name, n.Fields[j].Name) { + // we don't merge scalars with different onTypeNames to preserve the order of the fields + if !m.canMergeScalars(n.Fields[i], n.Fields[j]) { + continue + } + m.mergeScalars(n.Fields[i], n.Fields[j]) + n.Fields = append(n.Fields[:j], n.Fields[j+1:]...) + if i > j { + i-- + } + j-- + } + } + } + } for i := 0; i < len(n.Fields); i++ { m.traverseNode(n.Fields[i].Value) } @@ -85,6 +117,51 @@ func (m *MergeFields) traverseNode(node resolve.Node) { } } +func (m *MergeFields) canMergeScalars(left, right *resolve.Field) bool { + if left.OnTypeNames != nil && right.OnTypeNames != nil { + if !m.sameOnTypeNames(left.OnTypeNames, right.OnTypeNames) { + return false + } + } + return true +} + +func (m *MergeFields) mergeScalars(left, right *resolve.Field) { + // when left has no type conditions, it will overwrite right + if left.OnTypeNames == nil && left.ParentOnTypeNames == nil { + return + } + // when right has no type conditions, it will overwrite left + if right.OnTypeNames == nil && right.ParentOnTypeNames == nil { + left.OnTypeNames = nil + left.ParentOnTypeNames = nil + return + } + left.OnTypeNames = m.deduplicateOnTypeNames(append(left.OnTypeNames, right.OnTypeNames...)) + if left.ParentOnTypeNames == nil { + left.ParentOnTypeNames = right.ParentOnTypeNames + return + } + if right.ParentOnTypeNames == nil { + return + } +WithNext: + for i := range right.ParentOnTypeNames { + for j := range left.ParentOnTypeNames { + if right.ParentOnTypeNames[i].Depth == left.ParentOnTypeNames[j].Depth { + // merge all parent type conditions at the same depth + // this is important because resolvable.go ensures that at each depth layer, + // we have at least one matching type condition + // otherwise we skip resolving the field + left.ParentOnTypeNames[j].Names = m.deduplicateOnTypeNames(append(left.ParentOnTypeNames[j].Names, right.ParentOnTypeNames[i].Names...)) + continue WithNext + } + } + // if we reach this point, we have a new depth layer and just append it + left.ParentOnTypeNames = append(left.ParentOnTypeNames, right.ParentOnTypeNames[i]) + } +} + func (m *MergeFields) fieldsCanMerge(left *resolve.Field, right *resolve.Field) bool { if !bytes.Equal(left.Name, right.Name) { return false @@ -95,6 +172,11 @@ func (m *MergeFields) fieldsCanMerge(left *resolve.Field, right *resolve.Field) if !m.sameOnTypeNames(left.OnTypeNames, right.OnTypeNames) { return false } + // scalars with different parent type conditions can't be merged at this point + // we're handling this case later when we merge scalar fields + if m.nodeIsScalar(left.Value) && !m.sameParentOnTypeNames(left, right) { + return false + } return true } @@ -129,16 +211,37 @@ WithNext: return true } -func (m *MergeFields) mergeValues(left, right resolve.Node) { - switch v := left.(type) { - case *resolve.Object: - r := right.(*resolve.Object) - v.Fields = append(v.Fields, r.Fields...) - if r.Fetch != nil { - v.Fetch = r.Fetch +func (m *MergeFields) sameParentOnTypeNames(left, right *resolve.Field) bool { + if len(left.ParentOnTypeNames) != len(right.ParentOnTypeNames) { + return false + } + for i := range left.ParentOnTypeNames { + for j := range right.ParentOnTypeNames { + if left.ParentOnTypeNames[i].Depth != right.ParentOnTypeNames[j].Depth { + continue + } + if !m.sameOnTypeNames(left.ParentOnTypeNames[i].Names, right.ParentOnTypeNames[j].Names) { + continue + } + break } + return false + } + return true +} + +func (m *MergeFields) mergeValues(left, right *resolve.Field) { + switch l := left.Value.(type) { + case *resolve.Object: + r := right.Value.(*resolve.Object) + l.Fields = append(l.Fields, r.Fields...) case *resolve.Array: - m.mergeValues(v.Item, right.(*resolve.Array).Item) + r := right.Value.(*resolve.Array) + if l.Item.NodeKind() == resolve.NodeKindObject { + lo := l.Item.(*resolve.Object) + ro := r.Item.(*resolve.Object) + lo.Fields = append(lo.Fields, ro.Fields...) + } } } @@ -149,3 +252,38 @@ func (m *MergeFields) nodeIsScalar(node resolve.Node) bool { } return true } + +func (m *MergeFields) propagateParentTypeNames(field *resolve.Field) { + if field.OnTypeNames == nil { + return + } + m.setParentTypeNames(field, field.OnTypeNames, 1) +} + +// setParentTypeNames recursively sets the parent type names for all children of a field +// increasing the depth by 1 for each level +func (m *MergeFields) setParentTypeNames(field *resolve.Field, typeNames [][]byte, depth int) { + switch field.Value.NodeKind() { + case resolve.NodeKindObject: + object := field.Value.(*resolve.Object) + for i := range object.Fields { + object.Fields[i].ParentOnTypeNames = append(object.Fields[i].ParentOnTypeNames, resolve.ParentOnTypeNames{ + Depth: depth, + Names: typeNames, + }) + m.setParentTypeNames(object.Fields[i], typeNames, depth+1) + } + case resolve.NodeKindArray: + array := field.Value.(*resolve.Array) + if array.Item.NodeKind() == resolve.NodeKindObject { + object := array.Item.(*resolve.Object) + for i := range object.Fields { + object.Fields[i].ParentOnTypeNames = append(object.Fields[i].ParentOnTypeNames, resolve.ParentOnTypeNames{ + Depth: depth, + Names: typeNames, + }) + m.setParentTypeNames(object.Fields[i], typeNames, depth+1) + } + } + } +} diff --git a/v2/pkg/engine/postprocess/merge_fields_test.go b/v2/pkg/engine/postprocess/merge_fields_test.go new file mode 100644 index 000000000..4ac7e2c99 --- /dev/null +++ b/v2/pkg/engine/postprocess/merge_fields_test.go @@ -0,0 +1,229 @@ +package postprocess + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/wundergraph/graphql-go-tools/v2/pkg/engine/resolve" +) + +func TestMergeFields_Process(t *testing.T) { + + runTest := func(in, out resolve.Node) func(t *testing.T) { + return func(t *testing.T) { + m := &MergeFields{} + m.Process(in) + assert.Equal(t, out, in) + } + } + + t.Run("merge fields at the end of an object", runTest( + &resolve.Object{ + Fields: []*resolve.Field{ + { + Name: []byte(`a`), + Value: &resolve.Integer{}, + }, + { + Name: []byte(`a`), + Value: &resolve.Integer{}, + OnTypeNames: [][]byte{ + []byte(`A`), + }, + }, + }, + }, + &resolve.Object{ + Fields: []*resolve.Field{ + { + Name: []byte(`a`), + Value: &resolve.Integer{}, + }, + }, + }, + )) + + t.Run("merge fields at the end of an object reverse", runTest( + &resolve.Object{ + Fields: []*resolve.Field{ + { + Name: []byte(`a`), + Value: &resolve.Integer{}, + OnTypeNames: [][]byte{ + []byte(`A`), + }, + }, + { + Name: []byte(`a`), + Value: &resolve.Integer{}, + OnTypeNames: [][]byte{ + []byte(`A`), + }, + }, + { + Name: []byte(`a`), + Value: &resolve.Integer{}, + }, + }, + }, + &resolve.Object{ + Fields: []*resolve.Field{ + { + Name: []byte(`a`), + Value: &resolve.Integer{}, + }, + }, + }, + )) + + t.Run("merge fields at the end of an object nested", runTest( + &resolve.Object{ + Fields: []*resolve.Field{ + { + Name: []byte(`a`), + Value: &resolve.Object{ + Fields: []*resolve.Field{ + { + Name: []byte(`b`), + Value: &resolve.Integer{}, + }, + }, + }, + OnTypeNames: [][]byte{ + []byte(`A`), + }, + }, + { + Name: []byte(`a`), + Value: &resolve.Object{ + Fields: []*resolve.Field{ + { + Name: []byte(`b`), + Value: &resolve.Integer{}, + }, + }, + }, + }, + }, + }, + &resolve.Object{ + Fields: []*resolve.Field{ + { + Name: []byte(`a`), + Value: &resolve.Object{ + Fields: []*resolve.Field{ + { + Name: []byte(`b`), + Value: &resolve.Integer{}, + }, + }, + }, + }, + }, + }, + )) + + t.Run("merge fields at the end of an object nested reverse", runTest( + &resolve.Object{ + Fields: []*resolve.Field{ + { + Name: []byte(`a`), + Value: &resolve.Object{ + Fields: []*resolve.Field{ + { + Name: []byte(`b`), + Value: &resolve.Integer{}, + }, + }, + }, + }, + { + Name: []byte(`a`), + Value: &resolve.Object{ + Fields: []*resolve.Field{ + { + Name: []byte(`b`), + Value: &resolve.Integer{}, + }, + }, + }, + OnTypeNames: [][]byte{ + []byte(`A`), + }, + }, + }, + }, + &resolve.Object{ + Fields: []*resolve.Field{ + { + Name: []byte(`a`), + Value: &resolve.Object{ + Fields: []*resolve.Field{ + { + Name: []byte(`b`), + Value: &resolve.Integer{}, + }, + }, + }, + }, + }, + }, + )) + + t.Run("merge fields nested object differing onTypeNames", runTest( + &resolve.Object{ + Fields: []*resolve.Field{ + { + Name: []byte(`a`), + Value: &resolve.Object{ + Fields: []*resolve.Field{ + { + Name: []byte(`b`), + Value: &resolve.Integer{}, + }, + }, + }, + OnTypeNames: [][]byte{ + []byte(`A`), + }, + }, + { + Name: []byte(`a`), + Value: &resolve.Object{ + Fields: []*resolve.Field{ + { + Name: []byte(`c`), + Value: &resolve.Integer{}, + }, + }, + }, + OnTypeNames: [][]byte{ + []byte(`B`), + }, + }, + }, + }, + &resolve.Object{ + Fields: []*resolve.Field{ + { + Name: []byte(`a`), + Value: &resolve.Object{ + Fields: []*resolve.Field{ + { + Name: []byte(`b`), + Value: &resolve.Integer{}, + }, + { + Name: []byte(`c`), + Value: &resolve.Integer{}, + OnTypeNames: [][]byte{ + []byte(`A`), + }, + }, + }, + }, + }, + }, + }, + )) +} diff --git a/v2/pkg/engine/resolve/node_object.go b/v2/pkg/engine/resolve/node_object.go index f7118e416..42e4a24a2 100644 --- a/v2/pkg/engine/resolve/node_object.go +++ b/v2/pkg/engine/resolve/node_object.go @@ -91,6 +91,7 @@ type Field struct { Defer *DeferField Stream *StreamField OnTypeNames [][]byte + ParentOnTypeNames []ParentOnTypeNames SkipDirectiveDefined bool SkipVariableName string IncludeDirectiveDefined bool @@ -98,6 +99,11 @@ type Field struct { Info *FieldInfo } +type ParentOnTypeNames struct { + Depth int + Names [][]byte +} + func (f *Field) Copy() *Field { return &Field{ Name: f.Name, diff --git a/v2/pkg/engine/resolve/resolvable.go b/v2/pkg/engine/resolve/resolvable.go index 0b17b5ffb..3fc4784f7 100644 --- a/v2/pkg/engine/resolve/resolvable.go +++ b/v2/pkg/engine/resolve/resolvable.go @@ -8,9 +8,8 @@ import ( "fmt" "io" - "github.com/pkg/errors" - "github.com/cespare/xxhash/v2" + "github.com/pkg/errors" "github.com/tidwall/gjson" "github.com/wundergraph/graphql-go-tools/v2/pkg/ast" @@ -42,6 +41,8 @@ type Resolvable struct { wroteErrors bool wroteData bool + + typeNames [][]byte } func NewResolvable() *Resolvable { @@ -55,6 +56,7 @@ func NewResolvable() *Resolvable { func (r *Resolvable) Reset() { r.storage.Reset() + r.typeNames = r.typeNames[:0] r.wroteErrors = false r.wroteData = false r.dataRoot = -1 @@ -396,6 +398,11 @@ func (r *Resolvable) walkObject(obj *Object, ref int) bool { r.ctx.Stats.ResolvedObjects++ } addComma := false + typeName := r.getObjectTypeName(ref) + r.typeNames = append(r.typeNames, typeName) + defer func() { + r.typeNames = r.typeNames[:len(r.typeNames)-1] + }() for i := range obj.Fields { if obj.Fields[i].SkipDirectiveDefined { if r.skipField(obj.Fields[i].SkipVariableName) { @@ -407,8 +414,13 @@ func (r *Resolvable) walkObject(obj *Object, ref int) bool { continue } } + if obj.Fields[i].ParentOnTypeNames != nil { + if r.skipFieldOnParentTypeNames(obj.Fields[i]) { + continue + } + } if obj.Fields[i].OnTypeNames != nil { - if r.skipFieldOnTypeNames(ref, obj.Fields[i]) { + if r.skipFieldOnTypeNames(obj.Fields[i]) { continue } } @@ -553,17 +565,38 @@ func (r *Resolvable) objectFieldTypeName(ref int, field *Field) string { return field.Info.ExactParentTypeName } -func (r *Resolvable) skipFieldOnTypeNames(ref int, field *Field) bool { +func (r *Resolvable) getObjectTypeName(ref int) []byte { typeName := r.storage.GetObjectField(ref, "__typename") - if !r.storage.NodeIsDefined(typeName) { + if r.storage.NodeIsDefined(typeName) && r.storage.Nodes[typeName].Kind == astjson.NodeKindString { + return r.storage.Nodes[typeName].ValueBytes(r.storage) + } + return nil +} + +func (r *Resolvable) skipFieldOnParentTypeNames(field *Field) bool { +WithNext: + for i := range field.ParentOnTypeNames { + typeName := r.typeNames[len(r.typeNames)-1-field.ParentOnTypeNames[i].Depth] + if typeName == nil { + return true + } + for j := range field.ParentOnTypeNames[i].Names { + if bytes.Equal(typeName, field.ParentOnTypeNames[i].Names[j]) { + continue WithNext + } + } return true } - if r.storage.Nodes[typeName].Kind != astjson.NodeKindString { + return false +} + +func (r *Resolvable) skipFieldOnTypeNames(field *Field) bool { + typeName := r.typeNames[len(r.typeNames)-1] + if typeName == nil { return true } - value := r.storage.Nodes[typeName].ValueBytes(r.storage) for i := range field.OnTypeNames { - if bytes.Equal(value, field.OnTypeNames[i]) { + if bytes.Equal(typeName, field.OnTypeNames[i]) { return false } } From e4db380d7b86b02cdd2db68a49431799af69e144 Mon Sep 17 00:00:00 2001 From: Jens Neuse Date: Wed, 26 Jun 2024 16:00:39 +0200 Subject: [PATCH 2/4] chore: add comments --- v2/pkg/engine/resolve/resolvable.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/v2/pkg/engine/resolve/resolvable.go b/v2/pkg/engine/resolve/resolvable.go index 3fc4784f7..b5fa72aaa 100644 --- a/v2/pkg/engine/resolve/resolvable.go +++ b/v2/pkg/engine/resolve/resolvable.go @@ -578,15 +578,23 @@ WithNext: for i := range field.ParentOnTypeNames { typeName := r.typeNames[len(r.typeNames)-1-field.ParentOnTypeNames[i].Depth] if typeName == nil { + // The field has a condition but the JSON response object does not have a __typename field + // We skip this field return true } for j := range field.ParentOnTypeNames[i].Names { if bytes.Equal(typeName, field.ParentOnTypeNames[i].Names[j]) { + // on each layer of depth, we only need to match one of the names + // merge_fields.go ensures that we only have on ParentOnTypeNames per depth layer + // If we have a match, we continue WithNext condition until all layers have been checked continue WithNext } } + // No match at this depth layer, we skip this field return true } + // all layers have at least one matching typeName + // we don't skip this field (we return false) return false } From 1d50de5f8cf7de6d48d736b380c6e8ac7499bfe3 Mon Sep 17 00:00:00 2001 From: Jens Neuse Date: Wed, 26 Jun 2024 17:18:44 +0200 Subject: [PATCH 3/4] chore: remove old test --- .../engine/postprocess/merge_fields_test.go | 57 ------------------- 1 file changed, 57 deletions(-) diff --git a/v2/pkg/engine/postprocess/merge_fields_test.go b/v2/pkg/engine/postprocess/merge_fields_test.go index 4ac7e2c99..90893a018 100644 --- a/v2/pkg/engine/postprocess/merge_fields_test.go +++ b/v2/pkg/engine/postprocess/merge_fields_test.go @@ -169,61 +169,4 @@ func TestMergeFields_Process(t *testing.T) { }, }, )) - - t.Run("merge fields nested object differing onTypeNames", runTest( - &resolve.Object{ - Fields: []*resolve.Field{ - { - Name: []byte(`a`), - Value: &resolve.Object{ - Fields: []*resolve.Field{ - { - Name: []byte(`b`), - Value: &resolve.Integer{}, - }, - }, - }, - OnTypeNames: [][]byte{ - []byte(`A`), - }, - }, - { - Name: []byte(`a`), - Value: &resolve.Object{ - Fields: []*resolve.Field{ - { - Name: []byte(`c`), - Value: &resolve.Integer{}, - }, - }, - }, - OnTypeNames: [][]byte{ - []byte(`B`), - }, - }, - }, - }, - &resolve.Object{ - Fields: []*resolve.Field{ - { - Name: []byte(`a`), - Value: &resolve.Object{ - Fields: []*resolve.Field{ - { - Name: []byte(`b`), - Value: &resolve.Integer{}, - }, - { - Name: []byte(`c`), - Value: &resolve.Integer{}, - OnTypeNames: [][]byte{ - []byte(`A`), - }, - }, - }, - }, - }, - }, - }, - )) } From 5bd8e36842ffd841030d38f6b57b259f26af6b11 Mon Sep 17 00:00:00 2001 From: spetrunin Date: Wed, 26 Jun 2024 18:51:35 +0300 Subject: [PATCH 4/4] chore: remove err check --- execution/engine/federation_integration_static_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/execution/engine/federation_integration_static_test.go b/execution/engine/federation_integration_static_test.go index fa2e71a2d..d9d5048cc 100644 --- a/execution/engine/federation_integration_static_test.go +++ b/execution/engine/federation_integration_static_test.go @@ -96,8 +96,7 @@ subscription UpdatedPrice { }) go func() { - err := engine.Execute(execCtx, gqlRequest, &resultWriter) - require.NoError(t, err) + _ = engine.Execute(execCtx, gqlRequest, &resultWriter) }() assert.Eventuallyf(t, func() bool {