-
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
Plugin: ForceResolver #354
Conversation
codegen/plugins/resolver/plugin.go
Outdated
|
||
func (p *Plugin) Schema(cfg *codegen.Config) (*ast.Source, error) { | ||
return &ast.Source{ | ||
Name: "resolver plugin", |
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.
I wonder if this should be done by the caller and this function can just return the schema string?
codegen/plugins/resolver/plugin.go
Outdated
cfg.Directives["resolver"] = codegen.DirectiveMapEntry{Implementation: "github.com/99designs/gqlgen/codegen/plugins/resolver.DirectiveNoop"} | ||
for _, typ := range schema.Types { | ||
for _, f := range typ.Fields { | ||
if d := f.Directives.ForName("resolver"); d != nil { |
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.
if d == nil { continue }
?
"github.com/vektah/gqlparser/ast" | ||
) | ||
|
||
type Plugin interface { |
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.
Do we want one large interface with lots of methods or lots of small interfaces that can optionally be implemented?
eg we may eventually have codegen hooks here, but most plugins wont use them.
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.
lots of 1-function interfaces will be easier to use, then one big.
As I understand, in this realization, I need to rebuild gen every time I add new plugin? |
@vetcher theres a few different fronts that can be advanced here but its tricky. Right now, we are focusing on what the plugins can do rather then how they get injected. We've thought a little about using go plugins (and probably will support it in the future) but it adds a bunch of complexity:
For now we are just going to side step that complexity by building a bunch of plugins into the binary, and making it easy to build your own entry point. fun fact, |
@vektah you may add a line to config, name it func initPlugins(plugins []string) error {
for i := range plugins {
_, err := plugin.Open(plugins[i])
if err != nil {
return errors.Wrapf(err, "open plugin %s", plugins[i])
}
}
return nil
} And in plugin we just need to add 3 lines: func init() {
codegen.RegisterPlugin(plugin)
} It can be nice addition to current realisation and user does not need to recompile gqlgen each time. |
Improve grep-ability, since it took me a few mins to find with other usages of Resolver.
Take @vektah's suggestions, I've removed the global plugin registry and looked at implementing separate interfaces for each hook that a plugin might want to support.
f366877
to
86f984b
Compare
db7954f
to
b79a4e6
Compare
b79a4e6
to
9a38f5c
Compare
@vektah ok I've had another pass over this now. Key changes:
|
@@ -9,26 +9,26 @@ import ( | |||
"time" | |||
) | |||
|
|||
type resolver struct { | |||
type chat struct { |
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.
Maybe resolver
is too generic a plugin name; this clashed so I changed the resolver struct name here.
For context — I've closed this for now. We're thinking about doing a plugin release the one after next and focussing on it entirely. This has been around long enough now though that our thinking has shifted a little and we might approach this slightly differently. |
This is a first attempt to chip away at progressing a plugin system from #228. This implements a single plugin
resolver
that adds a directive@resolver
to the schema, and allows a user to tag fields in their schema as requiring a resolver; this is the same functionality exposed in config.Plugins
After a bit of discussion in #228 and elsewhere, our initial direction for supporting plugins will be to ship the
gqlgen
binary with a set of base plugins, and let users generate their own binary if they want to add additional plugins themselves.We considered some other directions, such as using the
plugin
package — but have decided to keep it simple while we're actually developing the capabilities of the system. Feedback is welcome here as to how this will fit into your existing workflows.Registration
Plugins are registered via
codegen.RegisterPlugin
and should satisfycodegen.Plugin
. It is currently expected that this would be done in a plugin package'sinit
function.Hooks
This PR adds 2 ways that plugins can hook into codegen functionality:
Plugin.Schema
should return an*ast.Source
(perhaps should be[]*ast.Source
?). These will be merged with the user's schema during parsing, allowing a plugin to add to a schema as it sees fit. Since this occurs before parsing, it may be too limiting for some plugin use-cases. We're investigating whether we need to separate out the parsing and validation steps in the parser.Plugin.Execute
is called after config normalization, and allows a plugin to arbitrarily modify configuration prior to codegen.These two hooks are sufficient for the
resolver
plugin to function, but we definitely understand that there will be lots of other use-cases that require additional hooks. My plan with this PR is to validate this general approach, and understand how we could expand (or even limit) it towards additional plugin use-cases.