-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
0.17.14 generate getters for interfaces problem #2331
Comments
I can of course work around this by removing a couple of the interfaces (Node, Edge, Connection). Looking at it right now they don't really give me any value. 🙈 |
@neptoess Can you look at the https://github.com/argoyle/gqlgen-implements repo and investigate what is happening with your change? I'm traveling and this is hard to look at on a phone. |
This is an interesting one. Looking at the schema (https://github.com/argoyle/gqlgen-implements/blob/main/graph/schema.graphqls), we have Even though I can see if the GraphQL spec calls this out. EDIT: It does https://spec.graphql.org/October2021/#sec-Objects. What you have here @argoyle is valid GraphQL. I can modify the getter generation logic for this scenario. |
I have a similar issue, but #2332 doesn't solve it. It happens when I try to upgrade gqlgen in https://github.com/MichaelMure/git-bug. Repro repo: https://github.com/MichaelMure/gqlgen-issue In short:
In that scenario, shouldn't gqlgen generate a resolver? If so, I could easily wrap the normal type in the lazy loading one, but gqlgen just fail. Am I doing something wrong? Any advises? |
@MichaelMure func (b Bug) IsAuthored()
func (b Bug) GetAuthor() *Identity { return &b.Author }
func (c Comment) IsAuthored()
func (c Comment) GetAuthor() *Identity { return &c.Author } EDIT: Just remembered that this is actually demonstrated in the Star Wars example (https://github.com/99designs/gqlgen/blob/master/_examples/starwars/models/model.go). I had to update that when I first added getter generation. |
@neptoess ha sorry, I didn't realize that my toy example was binding on the wrong types, which was making the problem go away. I updated it accordingly and noticed 3 different issues:
I feel like this generated interface can be useful in some scenario, but also break down and force bad design in others. Am I doing something wrong? Or is there a way to disable that getter generation? |
@MichaelMure I re-opened this issue because it looks like there may be some more rough edges to iron out. If we can't easily make the new generating getters for interfaces transparently painless for existing users, I'd like to default it to off and make a config option to allow people to opt-in to this new behavior while we continue to refine it. What do you think of that @neptoess ? |
I wouldn’t have an issue hiding this behind a config flag. I can look at Michael’s issue more in depth tonight or tomorrow morning, but, from what I see so far on my phone, it looks like, again, the custom implementation for the Go type isn’t actually implementing the generated Go interface. Out of curiosity, do you remember anything like this coming up when interface generation went from just aliasing |
FWIW it did feel weird to me at that time but the workaround was easy enough to implement (just add the empty sign post function). Now that comes with enforced types and no error, which can really mess with and existing code architecture. |
Yeah I see, in your case, why this is frustrating. For GraphQL clients, when they call a query that returns an interface, they can select the fields defined in that interface, without knowing what type is actually implementing that interface. The inspiration for adding the getters in Go was to allow those same kind of semantics, i.e. reading the values of fields defined in the interface, without unboxing to the concrete type. But your code is doing a lot more than just returning a field. The star wars example in the gqlgen repo has the same thing going on with fields like FriendsConnection. My “fix” there was to just turn those getters into what are effectively stubs (they just return I may be biased though 😛 |
At the end of the day, I very much appreciate you guy's work and consideration. |
The more I think about it, if we added a config option at this point we could default it to on by default, so we can let people opt out if it is problematic for them, but it would help encourage better practices for new people. I wonder how people who upgrade to the latest version and encounter a problem as @MichaelMure did would discover the solution/workaround. The compile time bugs like |
As I understand, generating getters is for convenience (?), but could you explain why those sign post functions are necessary? |
The sign posts were added quite some time ago to the Go interfaces generated by gqlgen to give the Go compiler some way to enforce type checking #335. Without getters, or the signpost functions, the interfaces were effectively just aliasing In theory, with getter generation enabled, the signposts are no longer needed for GraphQL interfaces. They would still be needed for GraphQL unions though. And, personally, I think it's pretty convenient having the signposts. Something worth pointing out is that the GraphQL and Go type systems have some pretty glaring differences, and, at least for me, it can be hard to know which mental model to reason with when working with Go code generated from a GraphQL schema. Since GraphQL requires explicitly stating that a type implements an interface in order for it to be used as that interface type, I think it does make sense to leave the signposts, even though Go does the whole implicit interface implementation thing. |
That's actually an excellent point. I'm not sure we've articulated the common friction points between the Go and GraphQL type systems, which might go a long way to explaining why we do some things the way we do. Do you mind listing some of the things you stumbled on? I'm worried I've gotten used to it for too long to remember all of what I found surprising. |
Not at all:
That's what I have off the top of my head. I haven't done anything too terribly exotic with gqlgen though (other than filling in schema.resolvers.go, it's just using whatever gqlgen generates), so I'm sure others have stumbled on bigger footguns. |
I asked some co-workers the same question and they added these:
|
Ok, I think I'm happy with where things are, but I would like a config option to disable generating getters for interfaces for people who run into similar inconvenience when upgrading. |
I can probably get around to that in the next week or two |
Thanks! I cut an interim release v0.17.15 that includes the latest refinement, and by the time you get a config option landed I can then cut a v0.17.16. I'm going to leave this issue open until then. |
One last note: this should have been more prominent in the release notes because it is a breaking change. Also probably should have justified a minor version bump. |
@StevenACoffman @neptoess slightly unrelated question but is it possible to generate getters for common fields for graphql union types or some way to tell gqlgen to add getters in union interface generated by gqlgen |
It would be pretty tricky. But, if a field is common among all types in a union, why not make an interface for them instead? |
@neptoess I am trying to stitch two graphql servers and one server is already having types defined as union and lots of old code is written around it so not feasible to change it rn and due to type inconsistencies, graphql stitching will fail. |
What happened?
When upgrading to 0.17.14 my code generation started to fail due to the new generation of getters for interfaces. It's due to interfaces returning other interfaces I think.
What did you expect?
Code generation to result in valid code
Minimal graphql.schema and models to reproduce
Reproduction repo: https://github.com/argoyle/gqlgen-implements
versions
go run github.com/99designs/gqlgen version
? 0.17.14go version
? 1.19The text was updated successfully, but these errors were encountered: