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
34 changes: 30 additions & 4 deletions v2/pkg/engine/plan/abstract_selection_rewriter_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -544,14 +544,14 @@ func (r *fieldSelectionRewriter) getAllowedInterfaceMemberTypeNames(fieldRef int
return nil, false, errors.New("unexpected error: field type name is not found in the upstream schema")
}

// if typename of a field is not equal to the typename of the interface type
// when typename of a field is not equal to the typename of the interface type
// then it should implement the interface type in the federated graph schema
if interfaceTypeName != fieldTypeName {
if slices.Contains(interfaceTypeNamesFromDefinition, fieldTypeName) {
return []string{fieldTypeName}, false, nil
}

// if it is not a member of the union type the config is corrupted
// if it doesn't implement an interface type the config is corrupted
return nil, false, errors.New("unexpected error: field type do not implement the interface in the federated graph schema")
}

Expand All @@ -560,10 +560,36 @@ func (r *fieldSelectionRewriter) getAllowedInterfaceMemberTypeNames(fieldRef int
return nil, false, errors.New("unexpected error: interface type definition not found in the upstream schema")
}

// in case node kind is an interface type definition we just return the implementing types in this datasource
// when node kind is an interface type definition
if interfaceNode.Kind == ast.NodeKindInterfaceTypeDefinition {
interfaceTypeNames, _ := r.upstreamDefinition.InterfaceTypeDefinitionImplementedByObjectWithNames(interfaceNode.Ref)
// we collect the implementing types in this datasource
localInterfaceTypeNames, _ := r.upstreamDefinition.InterfaceTypeDefinitionImplementedByObjectWithNames(interfaceNode.Ref)

interfaceTypeNames := make([]string, 0, len(localInterfaceTypeNames))

// additionally, we need to check if implementing types are interface objects
// and replace such typename with concrete types
for _, typeName := range localInterfaceTypeNames {
isInterfaceObject := false
// when typeName is an interface object, we need to add concrete types instead of the interface object type name
for _, k := range r.dsConfiguration.FederationConfiguration().InterfaceObjects {
if k.InterfaceTypeName == typeName {
interfaceTypeNames = append(interfaceTypeNames, k.ConcreteTypeNames...)
isInterfaceObject = true
break
}
}
// when typename is not an interface object, we can add it as is
if !isInterfaceObject {
interfaceTypeNames = append(interfaceTypeNames, typeName)
}
}

// sort implementing types to be able to compact them
sort.Strings(interfaceTypeNames)
// remove possible consecutive duplicates
interfaceTypeNames = slices.Compact(interfaceTypeNames)

return interfaceTypeNames, false, nil
}

Expand Down
87 changes: 87 additions & 0 deletions v2/pkg/engine/plan/abstract_selection_rewriter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3727,6 +3727,93 @@ func TestInterfaceSelectionRewriter_RewriteOperation(t *testing.T) {
}`,
shouldRewrite: false,
},
{
name: "field is an interface with concrete type fragments. one of implementing types is interface object",
definition: `
interface Named {
name: String!
}

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

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

interface Account implements Named {
id: ID!
name: String!
title: String!
}

type Query {
user: Named!
}`,
upstreamDefinition: `
interface Named {
name: String!
}

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

type Account implements Named @interfaceObject @key(fields: "id") {
id: ID!
name: String!
}

type Query {
user: Named!
}`,
dsConfiguration: dsb().
RootNode("Account", "id", "title").
RootNode("User", "id", "name", "surname").
WithMetadata(func(m *FederationMetaData) {
m.InterfaceObjects = []EntityInterfaceConfiguration{
{
InterfaceTypeName: "Account",
ConcreteTypeNames: []string{"Admin", "User"},
},
}
}).
DS(),
fieldName: "user",
operation: `
query {
__typename
user {
id
... on Account {
... on Admin {
title
}
}
}
}`,
expectedOperation: `
query {
__typename
user {
... on Admin {
id
title
}
... on User {
id
}
}
}`,
shouldRewrite: true,
},
}

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