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

Model plugin #502

Merged
merged 2 commits into from
Jan 26, 2019
Merged
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
6 changes: 3 additions & 3 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ func Execute() {
app.Before = func(context *cli.Context) error {
pwd, err := os.Getwd()
if err != nil {
return fmt.Errorf("unable to determine current workding dir: %s\n", err.Error())
return fmt.Errorf("unable to determine current workding dir: %s", err.Error())
}

if !gopath.Contains(pwd) {
return fmt.Errorf("gqlgen must be run from inside your $GOPATH\n")
return fmt.Errorf("gqlgen must be run from inside your $GOPATH")
}
if context.Bool("verbose") {
log.SetFlags(0)
Expand All @@ -47,7 +47,7 @@ func Execute() {
}

if err := app.Run(os.Args); err != nil {
fmt.Fprintf(os.Stderr, err.Error())
fmt.Fprint(os.Stderr, err.Error())
os.Exit(1)
}
}
45 changes: 6 additions & 39 deletions codegen/build.go
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
package codegen

import (
"fmt"
"go/types"
"sort"

"fmt"

"github.com/99designs/gqlgen/codegen/config"
"github.com/pkg/errors"
"github.com/vektah/gqlparser/ast"
"golang.org/x/tools/go/loader"
)

type builder struct {
Config *config.Config
Schema *ast.Schema
SchemaStr map[string]string
Program *loader.Program
Binder *config.Binder
Directives map[string]*Directive
NamedTypes NamedTypes
}
Expand All @@ -37,10 +35,9 @@ func buildSchema(cfg *config.Config) (*Schema, error) {
return nil, err
}

progLoader := b.Config.NewLoaderWithoutErrors()
b.Program, err = progLoader.Load()
b.Binder, err = b.Config.NewBinder()
if err != nil {
return nil, errors.Wrap(err, "loading failed")
return nil, err
}

b.NamedTypes = NamedTypes{}
Expand Down Expand Up @@ -120,7 +117,7 @@ func (b *builder) injectIntrospectionRoots(s *Schema) error {
return fmt.Errorf("root query type must be defined")
}

typeType, err := b.FindGoType("github.com/99designs/gqlgen/graphql/introspection", "Type")
typeType, err := b.Binder.FindObject("github.com/99designs/gqlgen/graphql/introspection", "Type")
if err != nil {
return errors.Wrap(err, "unable to find root Type introspection type")
}
Expand All @@ -145,7 +142,7 @@ func (b *builder) injectIntrospectionRoots(s *Schema) error {
Object: obj,
})

schemaType, err := b.FindGoType("github.com/99designs/gqlgen/graphql/introspection", "Schema")
schemaType, err := b.Binder.FindObject("github.com/99designs/gqlgen/graphql/introspection", "Schema")
if err != nil {
return errors.Wrap(err, "unable to find root Schema introspection type")
}
Expand All @@ -161,33 +158,3 @@ func (b *builder) injectIntrospectionRoots(s *Schema) error {

return nil
}

func (b *builder) FindGoType(pkgName string, typeName string) (types.Object, error) {
if pkgName == "" {
return nil, nil
}
fullName := typeName
if pkgName != "" {
fullName = pkgName + "." + typeName
}

pkgName, err := resolvePkg(pkgName)
if err != nil {
return nil, errors.Errorf("unable to resolve package for %s: %s\n", fullName, err.Error())
}

pkg := b.Program.Imported[pkgName]
if pkg == nil {
return nil, errors.Errorf("required package was not loaded: %s", fullName)
}

for astNode, def := range pkg.Defs {
if astNode.Name != typeName || def.Parent() == nil || def.Parent() != pkg.Pkg.Scope() {
continue
}

return def, nil
}

return nil, errors.Errorf("unable to find type %s\n", fullName)
}
6 changes: 4 additions & 2 deletions codegen/build_object.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"log"
"strings"

"github.com/99designs/gqlgen/codegen/templates"

"github.com/pkg/errors"
"github.com/vektah/gqlparser/ast"
)
Expand Down Expand Up @@ -79,7 +81,7 @@ func (b *builder) buildField(obj *Object, field *ast.FieldDefinition) (*Field, e
TypeReference: b.NamedTypes.getType(field.Type),
Object: obj,
Directives: dirs,
GoFieldName: lintName(ucFirst(field.Name)),
GoFieldName: templates.ToGo(field.Name),
GoFieldType: GoFieldVariable,
GoReceiverName: "obj",
}
Expand All @@ -99,7 +101,7 @@ func (b *builder) buildField(obj *Object, field *ast.FieldDefinition) (*Field, e
f.IsResolver = true
}
if typeField.FieldName != "" {
f.GoFieldName = lintName(ucFirst(typeField.FieldName))
f.GoFieldName = templates.ToGo(typeField.FieldName)
}
}
}
Expand Down
15 changes: 7 additions & 8 deletions codegen/build_typedef.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,16 @@ func (b *builder) buildTypeDef(schemaType *ast.Definition) (*TypeDefinition, err
}

// External marshal functions
def, _ := b.FindGoType(pkgName, "Marshal"+typeName)
def, err := b.Binder.FindObject(pkgName, typeName)
if err != nil {
return nil, err
}
if f, isFunc := def.(*types.Func); isFunc {
sig := def.Type().(*types.Signature)
t.GoType = sig.Params().At(0).Type()
t.Marshaler = f

unmarshal, err := b.FindGoType(pkgName, "Unmarshal"+typeName)
unmarshal, err := b.Binder.FindObject(pkgName, "Unmarshal"+typeName)
if err != nil {
return nil, errors.Wrapf(err, "unable to find unmarshal func for %s.%s", pkgName, typeName)
}
Expand All @@ -59,13 +62,9 @@ func (b *builder) buildTypeDef(schemaType *ast.Definition) (*TypeDefinition, err
}

// Normal object binding
obj, err := b.FindGoType(pkgName, typeName)
if err != nil {
return nil, errors.Wrapf(err, "unable to find %s.%s", pkgName, typeName)
}
t.GoType = obj.Type()
t.GoType = def.Type()

namedType := obj.Type().(*types.Named)
namedType := def.Type().(*types.Named)
hasUnmarshal := false
for i := 0; i < namedType.NumMethods(); i++ {
switch namedType.Method(i).Name() {
Expand Down
98 changes: 98 additions & 0 deletions codegen/config/binder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package config

import (
"fmt"
"go/types"
"regexp"
"strings"

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

// Binder connects graphql types to golang types using static analysis
type Binder struct {
program *loader.Program
types TypeMap
}

func (c *Config) NewBinder() (*Binder, error) {
conf := loader.Config{
AllowErrors: true,
TypeChecker: types.Config{
Error: func(e error) {},
},
}

for _, pkg := range c.Models.ReferencedPackages() {
conf.Import(pkg)
}

prog, err := conf.Load()
if err != nil {
return nil, errors.Wrap(err, "loading program")
}

return &Binder{
program: prog,
types: c.Models,
}, nil
}

func (b *Binder) FindType(pkgName string, typeName string) (types.Type, error) {
obj, err := b.FindObject(pkgName, typeName)
if err != nil {
return nil, err
}

if fun, isFunc := obj.(*types.Func); isFunc {
return fun.Type().(*types.Signature).Params().At(0).Type(), nil
}
return obj.Type(), nil
}

func (b *Binder) getPkg(find string) *loader.PackageInfo {
for n, p := range b.program.Imported {
if normalizeVendor(find) == normalizeVendor(n) {
return p
}
}
return nil
}

func (b *Binder) FindObject(pkgName string, typeName string) (types.Object, error) {
if pkgName == "" {
return nil, fmt.Errorf("package cannot be nil")
}
fullName := typeName
if pkgName != "" {
fullName = pkgName + "." + typeName
}

pkg := b.getPkg(pkgName)
if pkg == nil {
return nil, errors.Errorf("required package was not loaded: %s", fullName)
}

for astNode, def := range pkg.Defs {
// only look at defs in the top scope
if def == nil || def.Parent() == nil || def.Parent() != pkg.Pkg.Scope() {
continue
}

if astNode.Name == typeName || astNode.Name == "Marshal"+typeName {
return def, nil
}
}

return nil, errors.Errorf("unable to find type %s\n", fullName)
}

var modsRegex = regexp.MustCompile(`^(\*|\[\])*`)

func normalizeVendor(pkg string) string {
modifiers := modsRegex.FindAllString(pkg, 1)[0]
pkg = strings.TrimPrefix(pkg, modifiers)
parts := strings.Split(pkg, "/vendor/")
return modifiers + parts[len(parts)-1]
}
15 changes: 15 additions & 0 deletions codegen/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,12 @@ func (tm TypeMap) ReferencedPackages() []string {
return pkgs
}

func (tm TypeMap) Add(gqlName string, goType string) {
modelCfg := tm[gqlName]
modelCfg.Model = goType
tm[gqlName] = modelCfg
}

func inStrSlice(haystack []string, needle string) bool {
for _, v := range haystack {
if needle == v {
Expand Down Expand Up @@ -325,6 +331,15 @@ func (c *Config) normalize() error {
return nil
}

func (c *TypeMapEntry) PkgAndType() (string, string) {
parts := strings.Split(c.Model, ".")
if len(parts) == 1 {
return "", c.Model
}

return normalizeVendor(strings.Join(parts[:len(parts)-1], ".")), parts[len(parts)-1]
}

func (c *Config) LoadSchema() (*ast.Schema, map[string]string, error) {
schemaStrings := map[string]string{}

Expand Down
25 changes: 0 additions & 25 deletions codegen/config/loader.go

This file was deleted.

Loading