Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions v2/pkg/engine/plan/abstract_selection_rewriter.go
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,13 @@ func (r *fieldSelectionRewriter) interfaceFieldSelectionNeedsRewrite(selectionSe
if !r.allEntitiesHaveFieldsAsRootNode(entitiesWithoutFragment, selectionSetInfo.fields) {
return true
}

// check if any implementing type has requiresConfiguration for one of the requested fields
if slices.ContainsFunc(entityNames, func(entityName string) bool {
return r.hasRequiresConfigurationForField(entityName, selectionSetInfo.fields)
}) {
return true
}
}

if selectionSetInfo.hasFields && selectionSetInfo.hasInlineFragmentsOnObjects {
Expand Down
131 changes: 131 additions & 0 deletions v2/pkg/engine/plan/abstract_selection_rewriter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,83 @@ func TestInterfaceSelectionRewriter_RewriteOperation(t *testing.T) {
})
}

definitionC := `
interface Named {
name: String!
}

type User implements Named {
id: ID!
name: String!
surname: String!
}

type Admin implements Named {
id: ID!
name: String!
title: String!
}

type Query {
user: Named!
}`

upstreamDefinitionC := `
interface Named {
name: String!
}

type User implements Named @key(fields: "id") {
id: ID!
name: String! @requires(fields: "fullName")
fullName: String! @external
surname: String!
}

type Admin implements Named @key(fields: "id") {
id: ID!
name: String! @requires(fields: "fullName")
fullName: String! @external
}

type Query {
user: Named!
}`

dsBuilderC := func() *dsBuilder {
return dsb().
RootNode("Query", "user").
RootNode("User", "id", "name", "surname").
AddRootNodeExternalFieldNames("User", "fullName").
RootNode("Admin", "id", "name").
AddRootNodeExternalFieldNames("Admin", "fullName").
ChildNode("Named", "name").
WithMetadata(func(m *FederationMetaData) {
m.Requires = []FederationFieldConfiguration{
{
TypeName: "User",
FieldName: "name",
SelectionSet: "fullName",
},
{
TypeName: "Admin",
FieldName: "name",
SelectionSet: "fullName",
},
}
m.Keys = []FederationFieldConfiguration{
{
TypeName: "User",
SelectionSet: "id",
},
{
TypeName: "Admin",
SelectionSet: "id",
},
}
})
}

testCases := []testCase{
{
name: "should flatten interfaces for gRPC",
Expand Down Expand Up @@ -3943,6 +4020,60 @@ func TestInterfaceSelectionRewriter_RewriteOperation(t *testing.T) {
}`,
shouldRewrite: true,
},
{
name: "field is an interface members of which has requires directive. query do not have fragments",
definition: definitionC,
upstreamDefinition: upstreamDefinitionC,
dsBuilder: dsBuilderC(),
fieldName: "user",
operation: `
query {
user {
name
}
}`,
expectedOperation: `
query {
user {
... on Admin {
name
}
... on User {
name
}
}
}`,
shouldRewrite: true,
},
{
name: "field is an interface members of which has requires directive. query has concrete type fragment",
definition: definitionC,
upstreamDefinition: upstreamDefinitionC,
dsBuilder: dsBuilderC(),
fieldName: "user",
operation: `
query {
user {
name
... on User {
surname
}
}
}`,
expectedOperation: `
query {
user {
... on Admin {
name
}
... on User {
name
surname
}
}
}`,
shouldRewrite: true,
},
}

for _, testCase := range testCases {
Expand Down
Loading