Skip to content

Commit

Permalink
fix(GraphQL):This PR allows to use fragments on interfaces while quer…
Browse files Browse the repository at this point in the history
…ying other interface. (#6964)

Fixes GRAPHQL-863
Consider the below schema, Human implements both character and Employee interfaces.

interface Employee { ... }
interface Character { ... }
type Human implements Character & Employee { ... }
type Droid implements Character { ... }

While Querying Character interfaces which can return an object of Droid or Human, we will be able to use fragment on Employee interface if the returned object is of type Human and ignores it if the returned object is of type Droid.

(cherry picked from commit f810e4e)
  • Loading branch information
JatinDev543 committed Nov 24, 2020
1 parent e210274 commit 176d044
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 17 deletions.
25 changes: 25 additions & 0 deletions graphql/e2e/common/fragment.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,20 @@ func fragmentInQueryOnInterface(t *testing.T) {
n: name
}
}
qc3: queryCharacter {
... on Droid{
__typename
primaryFunction
}
... on Employee {
__typename
ename
}
... on Human {
__typename
name
}
}
qcRep1: queryCharacter {
name
... on Human {
Expand Down Expand Up @@ -316,6 +330,17 @@ func fragmentInQueryOnInterface(t *testing.T) {
{
}
],
"qc3":[
{
"__typename":"Human",
"ename":"Han_employee",
"name":"Han"
},
{
"__typename":"Droid",
"primaryFunction":"Robot"
}
],
"qcRep1": [
{
"name": "Han",
Expand Down
26 changes: 26 additions & 0 deletions graphql/resolve/query_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1935,6 +1935,32 @@
}
}
-
name: "fragment on interface implemented by type which implements multiple interfaces in query on some other interface"
gqlquery: |
query {
queryCharacter {
id
name
... on Employee {
ename
}
... on Human {
female
}
}
}
dgquery: |-
query {
queryCharacter(func: type(Character)) {
dgraph.type
id : uid
name : Character.name
ename : Employee.ename
female : Human.female
}
}
-
name: "Filter with id uses uid func at root."
gqlquery: |
Expand Down
26 changes: 9 additions & 17 deletions graphql/schema/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -275,28 +275,20 @@ func recursivelyExpandFragmentSelections(field *ast.Field, op *operation) {
satisfies := []string{typeName, ""}
var additionalTypes map[string]bool
switch typeKind {
case ast.Interface:
// expand fragments on types which implement this interface
case ast.Interface, ast.Union:
// expand fragments on types which implement this interface (for interface case)
// expand fragments on member types of this union (for Union case)
additionalTypes = getTypeNamesAsMap(op.inSchema.schema.PossibleTypes[typeName])
// if there is any fragment in the selection set of this field, need to store a mapping from
// fields in that fragment to the fragment's type condition, to be used later in completion.
for _, f := range field.SelectionSet {
addSelectionToInterfaceImplFragFields(typeName, f, additionalTypes, op)
}
case ast.Union:
// expand fragments on member types of this union
additionalTypes = getTypeNamesAsMap(op.inSchema.schema.PossibleTypes[typeName])
// also, expand fragments on interfaces which are implemented by the member types of this
// union
// also, expand fragments on interfaces which are implemented by the member types of this union
// And also on additional interfaces which also implement the same type
var interfaceFragsToExpand []*ast.Definition
for memberType := range additionalTypes {
for typ := range additionalTypes {
interfaceFragsToExpand = append(interfaceFragsToExpand,
op.inSchema.schema.Implements[memberType]...)
op.inSchema.schema.Implements[typ]...)
}
additionalInterfaces := getTypeNamesAsMap(interfaceFragsToExpand)
// for fragments in the selection set of this union field, need to store a mapping from
// fields in that fragment to the fragment's type condition, for each of the additional
// interfaces, to be used later in completion.
// if there is any fragment in the selection set of this field, need to store a mapping from
// fields in that fragment to the fragment's type condition, to be used later in completion.
for interfaceName := range additionalInterfaces {
additionalTypes[interfaceName] = true
for _, f := range field.SelectionSet {
Expand Down

0 comments on commit 176d044

Please sign in to comment.