Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for interfaces implementing other interfaces #436

Closed

Conversation

tomemmerson
Copy link

@tomemmerson tomemmerson commented Mar 5, 2021

To fix issue Add support for interfaces implementing other interfaces

To match new draft spec here: http://spec.graphql.org/draft/#sec-Interfaces.Interfaces-Implementing-Interfaces
Allows the following functionality:

interface Node {
  id: ID!
}

interface Resource implements Node {
  id: ID!
  url: String
}

The implementation here isn't massively efficient, I check multiple interfaces multiple times, mostly due to the requirement that

Transitively implemented interfaces (interfaces implemented by the interface that is being implemented) must also be defined on an implementing type or interface. For example, Image cannot implement Resource without also implementing Node:
https://spec.graphql.org/draft/#sel-FAHbhBHCAACGB35P

There's definitely a more efficient solution than the one here, really just wanted an opinion as to whether it is worth it.

Thanks
Tom

Tom Emmerson added 2 commits March 5, 2021 18:28
Interfaces now return an empty array for the "interfaces" introspection query
@tomemmerson tomemmerson marked this pull request as ready for review March 6, 2021 12:24
for _, f := range iface.Interfaces {
if f == nil {
continue
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this happen? Is it possible that one of the iface.Interfaces is nil?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, good point, ifaces should never be nil in the current implementation. Just used to checking pointer types, is there a reason all the types are using pointers or can I change Schema.Interfaces to a non-pointer type and remove this check?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what the reason is since I haven't written this code but my guess is that the author didn't want to copy structs by value for performance reasons. On the other hand, interfaces are not supposed to be huge. But we never know how people use it. So let's leave it consistent with all other types.

I'd prefer if both the interfaceDependancies and search functions accept a reference. Then at the beginning of the search function check if the passed pointer to the Interface struct is nil and if yes, then panic. This is never supposed to happen. I don't like that in the current implementation you keep them as pointers but then you dereference them and pass them as values.

for _, iface := range s.interfaces {
if iface == nil {
continue
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this ever be true? Can we have a nil interface?

actualNames := iface.interfaceNames

expMap := make(map[string]int)
actMap := make(map[string]int)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you, please, remove the Map suffix from the names. From the syntax below it is obvious that it is a map.

}
}

for _, iface := range s.interfaces {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In order to avoid checking interfaces multiple times, you might define a variable of type map[string][]string above this for statement that contains all the dependencies for each interface. Then you will check every interface exactly once if it is not in the map already.
However, if I understand correctly, you will need to sort all the interfaces by len(interfaceNames) ascending. This way interfaces that implement multiple other interfaces will make use of that map efficiently. This map will be used only once at runtime and then it will be garbage collected.

// given interface
//
// TODO: Would be more efficient not to search every interface multiple
// times.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my comment above how to handle that.

@pavelnikolov
Copy link
Member

There are conflicts

@suessflorian
Copy link
Contributor

suessflorian commented Oct 2, 2021

👋
What's the status with this PR? Any way I can help out? I just read through graphql/graphql-spec#373 this could actually be very useful on a project I'm working on so keen to see this getting implemented. Awesome work so far though @tomemmerson 👍

@pavelnikolov
Copy link
Member

@suessflorian I'd be happy to merge this change if you could resolve the conflicts

@tomemmerson
Copy link
Author

Apologies @suessflorian + @pavelnikolov - have been very busy on another project. Feel free to take it from where I left off.

Sorry,
Tom

@suessflorian
Copy link
Contributor

No worries @tomemmerson, your start here helped a lot - I've opened up a new PR continuing on from here and so I think we can close this PR @pavelnikolov 👍

@pavelnikolov
Copy link
Member

Closing this PR because of #471

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants