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

Plugin: ForceResolver #354

Closed
wants to merge 11 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions cmd/gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"os"

"github.com/99designs/gqlgen/codegen"
"github.com/99designs/gqlgen/plugins"
"github.com/pkg/errors"
"github.com/urfave/cli"
)
Expand Down Expand Up @@ -43,6 +44,8 @@ var genCmd = cli.Command{
}
config.SchemaStr = string(schemaRaw)

config.Plugins.Register(plugins.DefaultPlugins()...)

if err = config.Check(); err != nil {
fmt.Fprintln(os.Stderr, "invalid config format: "+err.Error())
os.Exit(1)
Expand Down
3 changes: 3 additions & 0 deletions cmd/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"strings"

"github.com/99designs/gqlgen/codegen"
"github.com/99designs/gqlgen/plugins"
"github.com/pkg/errors"
"github.com/urfave/cli"
"gopkg.in/yaml.v2"
Expand Down Expand Up @@ -143,6 +144,8 @@ func initConfig(ctx *cli.Context) *codegen.Config {
os.Exit(1)
}

config.Plugins.Register(plugins.DefaultPlugins()...)

return config
}

Expand Down
10 changes: 8 additions & 2 deletions codegen/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"os"

"github.com/pkg/errors"
"github.com/vektah/gqlparser/ast"
"golang.org/x/tools/go/loader"
)

Expand All @@ -19,9 +20,9 @@ type Build struct {
QueryRoot *Object
MutationRoot *Object
SubscriptionRoot *Object
SchemaRaw string
SchemaFilename string
Directives Directives
SchemaSources []*ast.Source
}

type ModelBuild struct {
Expand Down Expand Up @@ -132,6 +133,11 @@ func (cfg *Config) bind() (*Build, error) {
namedTypes := cfg.buildNamedTypes()

progLoader := newLoader(namedTypes, true)
for _, i := range cfg.Directives.Implementations() {
pkg, _ := pkgAndType(i)
progLoader.Import(pkg)
}

prog, err := progLoader.Load()
if err != nil {
return nil, errors.Wrap(err, "loading failed")
Expand Down Expand Up @@ -160,9 +166,9 @@ func (cfg *Config) bind() (*Build, error) {
Interfaces: cfg.buildInterfaces(namedTypes, prog),
Inputs: inputs,
Imports: imports.finalize(),
SchemaRaw: cfg.SchemaStr,
SchemaFilename: cfg.SchemaFilename,
Directives: directives,
SchemaSources: cfg.schemaSources,
}

if cfg.schema.Query != nil {
Expand Down
15 changes: 13 additions & 2 deletions codegen/codegen.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ func Generate(cfg Config) error {
return err
}

cfg.Plugins.postNormalize(&cfg, cfg.schema)

_ = syscall.Unlink(cfg.Exec.Filename)
_ = syscall.Unlink(cfg.Model.Filename)

Expand Down Expand Up @@ -151,11 +153,20 @@ func (cfg *Config) normalize() error {
}
}

var err *gqlerror.Error
cfg.schema, err = gqlparser.LoadSchema(&ast.Source{Name: cfg.SchemaFilename, Input: cfg.SchemaStr})
srcs := []*ast.Source{{Name: cfg.SchemaFilename, Input: cfg.SchemaStr}}

pluginSrcs, err := cfg.Plugins.schemas(cfg)
if err != nil {
return err
}
srcs = append(srcs, pluginSrcs...)

var gqlerr *gqlerror.Error
cfg.schema, gqlerr = gqlparser.LoadSchema(srcs...)
cfg.schemaSources = srcs
if gqlerr != nil {
return gqlerr
}
return nil
}

Expand Down
20 changes: 16 additions & 4 deletions codegen/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ func DefaultConfig() *Config {
SchemaFilename: "schema.graphql",
Model: PackageConfig{Filename: "models_gen.go"},
Exec: PackageConfig{Filename: "generated.go"},
Directives: make(DirectiveMap),
}
}

Expand Down Expand Up @@ -68,9 +69,11 @@ type Config struct {
StructTag string `yaml:"struct_tag,omitempty"`
Directives DirectiveMap `yaml:"directives,omitempty"`

FilePath string `yaml:"-"`
FilePath string `yaml:"-"`
Plugins PluginRegistry `yaml:"-"`

schema *ast.Schema `yaml:"-"`
schema *ast.Schema `yaml:"-"`
schemaSources []*ast.Source `yaml:"-"`
}

type PackageConfig struct {
Expand All @@ -85,8 +88,8 @@ type TypeMapEntry struct {
}

type TypeMapField struct {
Resolver bool `yaml:"resolver"`
FieldName string `yaml:"fieldName"`
ForceResolver bool `yaml:"resolver"`
FieldName string `yaml:"fieldName"`
}

func (c *PackageConfig) normalize() error {
Expand Down Expand Up @@ -177,6 +180,15 @@ func (dm DirectiveMap) ImplementationFor(name string) string {
return d.Implementation
}

func (dm DirectiveMap) Implementations() (impls []string) {
for _, d := range dm {
if d.Implementation != "" {
impls = append(impls, d.Implementation)
}
}
return impls
}

// findCfg searches for the config file in this directory and all parents up the tree
// looking for the closest match
func findCfg() (string, error) {
Expand Down
2 changes: 1 addition & 1 deletion codegen/object_build.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ func (cfg *Config) buildObject(types NamedTypes, typ *ast.Definition, imports *I
if entryExists {
if typeField, ok := typeEntry.Fields[field.Name]; ok {
goName = typeField.FieldName
forceResolver = typeField.Resolver
forceResolver = typeField.ForceResolver
}
}

Expand Down
56 changes: 56 additions & 0 deletions codegen/plugin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package codegen

import (
"github.com/vektah/gqlparser/ast"
)

// Plugin is an interface for a gqlgen plugin
type Plugin interface {
Copy link
Collaborator

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.

Copy link
Contributor

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.

Name() string
}

// PluginConfigurer is an interface a plugin can satisfy in order to make changes to configuration before codegen
type PluginConfigurer interface {
PostNormalize(c *Config, schema *ast.Schema) error
}

// PluginSchema is an interface a plugin can satisfy if they wish to merge additional schema with the base schema
type PluginSchema interface {
Schema(cfg *Config) (string, error)
}

// PluginRegistry is a collection of plugins that will be accessed during codegen
type PluginRegistry struct {
plugins []Plugin
}

// Register a set of plugins to the plugin registry
func (r *PluginRegistry) Register(p ...Plugin) {
r.plugins = append(r.plugins, p...)
}

func (r *PluginRegistry) schemas(c *Config) (srcs []*ast.Source, err error) {
for _, p := range r.plugins {
name := p.Name()
if p, ok := p.(PluginSchema); ok {
src, err := p.Schema(c)
if err != nil {
return nil, err
}
srcs = append(srcs, &ast.Source{Name: name, Input: src})
}
}
return srcs, err
}

func (r *PluginRegistry) postNormalize(cfg *Config, schema *ast.Schema) error {
for _, p := range r.plugins {
if p, ok := p.(PluginConfigurer); ok {
err := p.PostNormalize(cfg, schema)
if err != nil {
return err
}
}
}
return nil
}
2 changes: 1 addition & 1 deletion codegen/templates/data.go

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion codegen/templates/generated.gotpl
Original file line number Diff line number Diff line change
Expand Up @@ -276,5 +276,7 @@ func (ec *executionContext) introspectType(name string) *introspection.Type {
}

var parsedSchema = gqlparser.MustLoadSchema(
&ast.Source{Name: {{.SchemaFilename|quote}}, Input: {{.SchemaRaw|rawQuote}}},
{{- range $source := .SchemaSources }}
&ast.Source{Name: {{$source.Name|quote}}, Input: {{$source.Input|rawQuote}}},
{{- end }}
)
9 changes: 8 additions & 1 deletion codegen/testserver/generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 0 additions & 2 deletions codegen/testserver/gqlgen.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ models:
model: "github.com/99designs/gqlgen/codegen/testserver.Rectangle"
ForcedResolver:
model: "github.com/99designs/gqlgen/codegen/testserver.ForcedResolver"
fields:
field: { resolver: true }
User:
fields:
friends: { resolver: true }
Expand Down
2 changes: 1 addition & 1 deletion codegen/testserver/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ type Rectangle implements Shape {
union ShapeUnion = Circle | Rectangle

type ForcedResolver {
field: Circle
field: Circle @resolver
}

type EmbeddedPointer {
Expand Down
12 changes: 12 additions & 0 deletions example/chat/generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 8 additions & 8 deletions example/chat/resolvers.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,26 @@ import (
"time"
)

type resolver struct {
type chat struct {
Copy link
Contributor Author

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.

Rooms map[string]*Chatroom
mu sync.Mutex // nolint: structcheck
}

func (r *resolver) Mutation() MutationResolver {
func (r *chat) Mutation() MutationResolver {
return &mutationResolver{r}
}

func (r *resolver) Query() QueryResolver {
func (r *chat) Query() QueryResolver {
return &queryResolver{r}
}

func (r *resolver) Subscription() SubscriptionResolver {
func (r *chat) Subscription() SubscriptionResolver {
return &subscriptionResolver{r}
}

func New() Config {
return Config{
Resolvers: &resolver{
Resolvers: &chat{
Rooms: map[string]*Chatroom{},
},
}
Expand All @@ -40,7 +40,7 @@ type Chatroom struct {
Observers map[string]chan Message
}

type mutationResolver struct{ *resolver }
type mutationResolver struct{ *chat }

func (r *mutationResolver) Post(ctx context.Context, text string, username string, roomName string) (Message, error) {
r.mu.Lock()
Expand All @@ -67,7 +67,7 @@ func (r *mutationResolver) Post(ctx context.Context, text string, username strin
return message, nil
}

type queryResolver struct{ *resolver }
type queryResolver struct{ *chat }

func (r *queryResolver) Room(ctx context.Context, name string) (*Chatroom, error) {
r.mu.Lock()
Expand All @@ -81,7 +81,7 @@ func (r *queryResolver) Room(ctx context.Context, name string) (*Chatroom, error
return room, nil
}

type subscriptionResolver struct{ *resolver }
type subscriptionResolver struct{ *chat }

func (r *subscriptionResolver) MessageAdded(ctx context.Context, roomName string) (<-chan Message, error) {
r.mu.Lock()
Expand Down
Loading