-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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 lint-friendly small interfaces option for resolvers #134
Conversation
- prevents possible collision with an object type named `Short`
@vektah What do you think of this approach overall, worth pursuing? Also, I kept the implementation simple (just upper-cased the first letter of the field resolver) but I didn't see the |
I don't think I want to maintaining code between two different ways of doing this, there should be one good way to do this. I am open to changing the core implementation though, and this does look pretty nice. I'm more interested in the changes to user code that this would cause, and some examples of that would be great. I might try converting one of our codebases over to see what it looks like. |
Fair point. If it is introduced as a breaking change we should make sure it plays well with dep and such so things don't break for people updating though. I think we should be careful/thoughtful of the migration path, so as to not hurt adoption and set precedence for future changes as they may happen. And as far as that goes (and thus the changes to user code) I'm thinking:
Maybe we can try looking at usage around github. If people have a single struct with all methods, for example, something not too unlike what's in this PR would work. If there are projects that break the resolver into pieces, using embedded structs to put them together, it's a little more complex, but still possible. It could be a fun problem to solve (not sarcastic) :) |
Fwiw, I think this looks kind of exciting.
I recently migrated a pretty good sized project from the neelance libraries
to this one. (It was a really excellent change and lots of people were
very happy :)) It was also a project that had several different teams
operating in the code base, which made for an interesting example.
The main challenge in the migration process was trying to keep things
incremental. I ended up basically making a big dang struct with a bunch of
`panic("TODO")` methods to get the compiler to hush enough to let me focus
on getting other pieces in place. Smaller interfaces seem like they
might've been a helpful guiderail.
We also have several different packages (roughly aligning with the
different teams, per Conway's Law) in this project. And giving each
package all of its own interfaces would neat. (We have something like the
embedded structs situation already, but making the connection between the
generated Resolver interfaces and those structs still sounds like it would
be an improvement in clarity.)
(P.S. Hi Nate, fancy running into you here! 👋)
…On Thu, Jun 21, 2018 at 3:29 PM, Nathaniel Caza ***@***.***> wrote:
Fair point. If it is introduced as a breaking change we should make sure
it plays well with dep and such so things don't break for people updating
though. I think we should be careful/thoughtful of the migration path, so
as to not hurt adoption and set precedence for future changes as they may
happen.
And as far as that goes (and thus the changes to user code) I'm thinking:
- Offer a go fix equivalent that will refactor existing code (would
require some research, but maybe the most painless for users)
- Deprecate MakeExecutableSchema, make a new method, and offer a way
to generate a "shim" (inverse of this PR, old interface in - new interface
out), projects can upgrade on their time
- Release as a new major version, and if no tooling, offer suggestions
on how to restructure/refactor existing code
Maybe we can try looking at usage around github. If people have a single
struct with all methods, for example, something not too unlike what's in
this PR would work. If there are projects that break the resolver into
pieces, using embedded structs to put them together, it's a little more
complex, but still possible.
It could be a fun problem to solve (not sarcastic) :)
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
<#134 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/Afho_7iCQLl4msbpyQOy2esqbn6_XHIhks5t-5_VgaJpZM4UqJJz>
.
|
I think breaks are fine while gqlgen is still 0.x provided that:
This sounds good, lets land this so people can start opting into the new behaviour and work on the rest as a separate piece.
First thought is this is probably overkill, but it does feed really well into #9. There are a few codemods that would make working with gqlgen nicer:
|
@vektah Just to make sure I understand, for this PR:
Sound right? Also:
|
👌
Yeah, lets mark it as deprecated and maybe a link pointing back here or to the issue. There are some doc updates here too, I wonder if they should land as part of this PR or in a follow up? |
Updated, and went with For the doc updates, a case could be made either way. One reason for follow up work would be to give some time to determine what the "recommended" structure/conventions should be with the new interface. |
On second thought, maybe updating the examples is warranted:
:) |
Yeah, I think that makes sense. Lets leave it a bit and give people a chance to try it. It probably makes sense to do the deprecation/docs/examples all together. Lets pull the deprecation out for now so this can land cleanly on its own. |
- t.Parallel() doesn't guarantee parallel execution - moved goroutine so the test can execute independently
Did a bit of testing on our test branch today, seems to be good. |
postErrCh := make(chan error) | ||
go func() { | ||
var resp interface{} | ||
// can't call t.Fatal from separate goroutine, so we return to the err chan for later |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can't call fatal (because it relies on panic handlers...) but you can call t.Error and friends (assert.Equal, instead of require.Equal).
I think this can go back to the way it was, except using go func
, and assert
instead of t.Run
and require.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I think that comment might be a little misleading (see: oops forgot to update).
The issue we saw locally (after moving out of t.Run
) was that sub.Next
would hang indefinitely (or until timeout) if the mutation call failed (we did a negative test by making the mutation invalid to see) because it never gets any message.
The real reason it's there (and what that comment probably should say) is so we can use select
to wait for either sub.Next
or the Post
call to fail and then exit to avoid the deadlock.
Nice one @mastercactapus |
add lint-friendly small interfaces option for resolvers
This PR adds a
ShortResolvers
interface togenerated.go
allowing lint-friendly resolver methods. There is also a utility method that can take aShortResolvers
and return aResolvers
interface.The new interface is a possible solution to the linting issues raised in #106. It provides a lint-friendly path to implementing resolvers via smaller type-specific interfaces for each object type.
The implementation defines methods that return resolvers for each object type from the base. Each type (if it has resolver fields) additionally has a
TypeNameResolver
interface whereTypeName
would be the object type (e.g.Query
).No changes were made to the existing interface/implementation. To use the new one, a
FromShort
method will map aShortResolvers
to the existingResolvers
interface. That way the existingResolvers
interface remains the only necessary input type. This also makes it opt-in without breaking backwards compatibility, or adding additional maintenance to the existing code.The "smaller interfaces" example from #106 (comment) could then be used as follows:
FromShort
takes in theApp
(which fulfills the small interfaces needed byShortResolvers
) and returns the present-dayResolvers
interface.Generated Code Example
Existing interface (unchanged):
Net new code (example) in
generated.go
I'm not particularly attached to the name(s), just tried to keep it distinct and descriptive and picked something I could come up with in a reasonable amount of time. Suggestions welcome :)
EDIT: Switched from
ShortResolver
toShortResolvers
to prevent possible collision with an object namedShort