GraphQL Fragments generates unexpected behaviour

I have a schema like:

interface WithStaff {
  staff: [Collaboration!]!
}
interface Metadata {
  id: String! @id @search(by: [hash])
  createdAt: DateTime!
  modifiedAt: DateTime!
  generation: Int!
  version: Int!
}
interface Collaborator {
  collaborations: [Collaboration!]!
}

type Anime implements Metadata & WithStaff {
  name: String
}

type Collaboration implements Metadata {
  """
  Organization or Person
  """
  collaborator: Collaborator! @hasInverse(field: collaborations)
  """
  Episode, Anime, Manga, Volume, Chapter, ...
  """
  content: WithStaff! @hasInverse(field: staff)
}

type Organization implements Metadata & Collaborator {
  foundation: Date
  name: String
}

type Person implements Metadata & Collaborator {
   name: String
}

if I run the query

query {
    queryAnime(filter: {id: {eq: "AS0N2fOigo8X-ViL"}}) {
            staff {
                collaborator {
                    __typename
                    ... on Organization {
                        id
                    }
                }
            }
        }
    }

I expect to get all the organization involved with this anime (reading the GraphQL doc when a fragment is specified, it returns the fields inside of the fragment only for the entities which are of the fragment type)
Instead i get

{
  "data": {
    "queryAnime": [
      {
        "staff": [
          {
            "collaborator": {
              "__typename": "Organization",
              "id": "AS0vZTJ78C7wLIb3"
            }
          },
          {
            "collaborator": {
              "__typename": "Organization",
              "id": "AS0i6UMuZ7Fd2BqJ"
            }
          },
          {
            "collaborator": {
              "__typename": "Person",
              "id": "AS0MqA6d8p7Rwpq_"
            }
          },
          {
            "collaborator": {
              "__typename": "Person",
              "id": "AS0CDrNrC4LIUsJZ"
            }
          },
          {
            "collaborator": {
              "__typename": "Person",
              "id": "AS0ZAJZC77Rys1gU"
            }
          },
          {
            "collaborator": {
              "__typename": "Organization",
              "id": "AS0OqJKJ4lTUKSrs"
            }
          },
          {
            "collaborator": {
              "__typename": "Person",
              "id": "AS0Q7qCjHlmjlJ8K"
            }
          },
          {
            "collaborator": {
              "__typename": "Person",
              "id": "AS0_89Yzr8G8lIZk"
            }
          },
          {
            "collaborator": {
              "__typename": "Person",
              "id": "AS0_89Yzr8G8lIZk"
            }
          },
          {
            "collaborator": {
              "__typename": "Person",
              "id": "AS0USJ2_3ePCXWZ9"
            }
          },
          {
            "collaborator": {
              "__typename": "Person",
              "id": "AS05buZfC262v7AQ"
            }
          },
          {
            "collaborator": {
              "__typename": "Person",
              "id": "AS0R2qEUF3PSQEh2"
            }
          }
        ]
      }
    ]
  },
  "extensions": {
    "touched_uids": 60
  }
}

Wich is a mix between all the entities, regardless the type.
I think it’s a bug derived from the fact that both Organization and Person implement the Metadata interface and both have the id field. I’m not an ace with GraphQL, but i feel like the objects returned should depend on the fragment declaration and not if the underlying entity satisfies or not the fields inside the fragment itself

This was new to me. I did not know that you could implement on more than a single interface at a time. Thx!

it seems like this is completely ignored. This is another new syntax to me though. According the the Apollo GraphQL docs this should yield the results you expect.

To drive your point home with another example for the devs, in the Apollo GraphQL docs playground from the link above. If you provide this query:

query HeroForEpisode($ep: Episode!) {
  hero(episode: $ep) {
    __typename
    ... on Human {
      id
    }
  }
}

with variables:

{
  "ep": "JEDI"
}

It yields the results:

{
  "data": {
    "hero": {
      "__typename": "Droid"
    }
  }
}

If you will notice that the typename is shown but the id is not shown because the type is not Human even though the id is a valid property on the Droid as well.

Yw :slight_smile: you should also be able to implement multiple interfaces in an interface

interface Anime implements Metadata & WithStaff {

but is not yet implemented in dgraph… But that’s another story

About the fragment, I was referring to that guide, that’s why I would expect the fragment to “filter” the entities

2 Likes

As a side note, interfaces and types that implement interfaces do not yet support the @auth directive. Might want to keep that in mind if you are planning on going down that route like I am.

auth is supported an on types, but interfaces (and the types that implement them) don’t correctly support auth in the current beta.
https://graphql.dgraph.io/authorization/

This looks like a bug. We are going to look into this. Thanks for reporting it.

1 Like

Yeah, that’s right. Interfaces are a bit of rabbit hole.

For the 20.07.0 release, auth will work on all types, but not at all on interfaces. That quote from the docs needs a little updating. I think the situation for 20.07.0 will be that auth is implemented on all types, for queries and mutations, including when the type implements an interface, but not on the interface itself.

So for example if types TheTypeA and TheTypeB implements interface TheInterface, then you can put auth on both TheTypeA and TheTypeB, but not at all on TheInterface, and I think if we find auth on the concrete types, we’ll remove the queries and mutations for the interface because they aren’t protected by the auth.

The auth moving up to the interfaces is one of the first roadmap items for 20.11.0

2 Likes

That would be perfect for my use case!

should I open a ticket on github?

You don’t need to for now. This should be simple enough to fix without a GitHub issue. We are looking into it and will have a fix for you next week.

Did this ever get resolved? Just clarifying to whether or not it will continue to cause bugs

No, the behaviour is still present in master release.
Whatever type resolves the body of the fragment, get returned even if it is not the right type

@pawan, any ETA?

@abhimanyusinghgaur is currently working on this. I expect it to be resolved in a week.

1 Like

Hi @Luscha @amaster507
It has been fixed with this PR and is available in master. Will be part of release v20.7.2.

2 Likes