Skip to content

Commit

Permalink
add resolver layout = follow-schema
Browse files Browse the repository at this point in the history
  • Loading branch information
vektah committed Feb 3, 2020
1 parent 8a18895 commit 9e3b399
Show file tree
Hide file tree
Showing 6 changed files with 307 additions and 109 deletions.
84 changes: 7 additions & 77 deletions codegen/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package config

import (
"fmt"
"go/types"
"io/ioutil"
"os"
"path/filepath"
Expand All @@ -23,7 +22,7 @@ type Config struct {
SchemaFilename StringList `yaml:"schema,omitempty"`
Exec PackageConfig `yaml:"exec"`
Model PackageConfig `yaml:"model,omitempty"`
Resolver PackageConfig `yaml:"resolver,omitempty"`
Resolver ResolverConfig `yaml:"resolver,omitempty"`
AutoBind []string `yaml:"autobind"`
Models TypeMap `yaml:"models,omitempty"`
StructTag string `yaml:"struct_tag,omitempty"`
Expand Down Expand Up @@ -138,12 +137,6 @@ func LoadConfig(filename string) (*Config, error) {
return config, nil
}

type PackageConfig struct {
Filename string `yaml:"filename,omitempty"`
Package string `yaml:"package,omitempty"`
Type string `yaml:"type,omitempty"`
}

type TypeMapEntry struct {
Model StringList `yaml:"model"`
Fields map[string]TypeMapField `yaml:"fields,omitempty"`
Expand Down Expand Up @@ -184,90 +177,27 @@ func (a StringList) Has(file string) bool {
return false
}

func (c *PackageConfig) normalize() error {
if c.Filename == "" {
return errors.New("Filename is required")
}
c.Filename = abs(c.Filename)
// If Package is not set, first attempt to load the package at the output dir. If that fails
// fallback to just the base dir name of the output filename.
if c.Package == "" {
c.Package = code.NameForDir(c.Dir())
}

return nil
}

func (c *PackageConfig) ImportPath() string {
return code.ImportPathForDir(c.Dir())
}

func (c *PackageConfig) Dir() string {
return filepath.Dir(c.Filename)
}

func (c *PackageConfig) Check() error {
if strings.ContainsAny(c.Package, "./\\") {
return fmt.Errorf("package should be the output package name only, do not include the output filename")
}
if c.Filename != "" && !strings.HasSuffix(c.Filename, ".go") {
return fmt.Errorf("filename should be path to a go source file")
}

return c.normalize()
}

func (c *PackageConfig) Pkg() *types.Package {
return types.NewPackage(c.ImportPath(), c.Dir())
}

func (c *PackageConfig) IsDefined() bool {
return c.Filename != ""
}

func (c *Config) Check() error {
filesMap := make(map[string]bool)
pkgConfigsByDir := make(map[string]*PackageConfig)

if err := c.Models.Check(); err != nil {
return errors.Wrap(err, "config.models")
}
if err := c.Exec.Check(); err != nil {
if err := c.Exec.Check(filesMap, pkgConfigsByDir); err != nil {
return errors.Wrap(err, "config.exec")
}
if c.Model.IsDefined() {
if err := c.Model.Check(); err != nil {
if err := c.Model.Check(filesMap, pkgConfigsByDir); err != nil {
return errors.Wrap(err, "config.model")
}
}
if c.Resolver.IsDefined() {
if err := c.Resolver.Check(); err != nil {
if err := c.Resolver.Check(filesMap, pkgConfigsByDir); err != nil {
return errors.Wrap(err, "config.resolver")
}
}

// check packages names against conflict, if present in the same dir
// and check filenames for uniqueness
packageConfigList := []PackageConfig{}
if c.Model.IsDefined() {
packageConfigList = append(packageConfigList, c.Model)
}
packageConfigList = append(packageConfigList, []PackageConfig{
c.Exec,
c.Resolver,
}...)
filesMap := make(map[string]bool)
pkgConfigsByDir := make(map[string]PackageConfig)
for _, current := range packageConfigList {
_, fileFound := filesMap[current.Filename]
if fileFound {
return fmt.Errorf("filename %s defined more than once", current.Filename)
}
filesMap[current.Filename] = true
previous, inSameDir := pkgConfigsByDir[current.Dir()]
if inSameDir && current.Package != previous.Package {
return fmt.Errorf("filenames %s and %s are in the same directory but have different package definitions", stripPath(current.Filename), stripPath(previous.Filename))
}
pkgConfigsByDir[current.Dir()] = current
}

return c.normalize()
}

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

import (
"fmt"
"go/types"
"path/filepath"
"strings"

"github.com/99designs/gqlgen/internal/code"
)

type PackageConfig struct {
Filename string `yaml:"filename,omitempty"`
Package string `yaml:"package,omitempty"`
Type string `yaml:"type,omitempty"`
}

func (c *PackageConfig) normalize() error {
if c.Filename != "" {
c.Filename = abs(c.Filename)
}
// If Package is not set, first attempt to load the package at the output dir. If that fails
// fallback to just the base dir name of the output filename.
if c.Package == "" {
c.Package = code.NameForDir(c.Dir())
}

return nil
}

func (c *PackageConfig) ImportPath() string {
return code.ImportPathForDir(c.Dir())
}

func (c *PackageConfig) Dir() string {
return filepath.Dir(c.Filename)
}

func (c *PackageConfig) Pkg() *types.Package {
return types.NewPackage(c.ImportPath(), c.Dir())
}

func (c *PackageConfig) IsDefined() bool {
return c.Filename != ""
}

func (c *PackageConfig) Check(filesMap map[string]bool, pkgConfigsByDir map[string]*PackageConfig) error {
if err := c.normalize(); err != nil {
return err
}
if strings.ContainsAny(c.Package, "./\\") {
return fmt.Errorf("package should be the output package name only, do not include the output filename")
}
if c.Filename != "" && !strings.HasSuffix(c.Filename, ".go") {
return fmt.Errorf("filename should be path to a go source file")
}

_, fileFound := filesMap[c.Filename]
if fileFound {
return fmt.Errorf("filename %s defined more than once", c.Filename)
}
filesMap[c.Filename] = true
previous, inSameDir := pkgConfigsByDir[c.Dir()]
if inSameDir && c.Package != previous.Package {
return fmt.Errorf("filenames %s and %s are in the same directory but have different package definitions (%s vs %s)", stripPath(c.Filename), stripPath(previous.Filename), c.Package, previous.Package)
}
pkgConfigsByDir[c.Dir()] = c
return nil
}
71 changes: 71 additions & 0 deletions codegen/config/resolver.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package config

import (
"fmt"
"go/types"
"path/filepath"

"github.com/99designs/gqlgen/internal/code"
)

type ResolverConfig struct {
PackageConfig `yaml:",inline"`
Layout ResolverLayout `yaml:"layout,omitempty"`
DirName string `yaml:"dir"`
}

type ResolverLayout string

var (
LayoutSingleFile ResolverLayout = "single-file"
LayoutFollowSchema ResolverLayout = "follow-schema"
)

func (r *ResolverConfig) Check(filesMap map[string]bool, pkgConfigsByDir map[string]*PackageConfig) error {
if r.DirName != "" {
r.DirName = abs(r.DirName)
}

if r.Layout == "" {
r.Layout = "single-file"
}
if r.Layout != LayoutFollowSchema && r.Layout != LayoutSingleFile {
return fmt.Errorf("invalid layout %s. must be single-file or follow-schema", r.Layout)
}

if r.Layout == "follow-schema" && r.DirName == "" {
return fmt.Errorf("must specify dir when using laout:follow-schema")
}

return r.PackageConfig.Check(filesMap, pkgConfigsByDir)
}

func (r *ResolverConfig) ImportPath() string {
return code.ImportPathForDir(r.Dir())
}

func (r *ResolverConfig) Dir() string {
switch r.Layout {
case LayoutSingleFile:
return filepath.Dir(r.Filename)
case LayoutFollowSchema:
return r.DirName
default:
panic("invalid layout " + r.Layout)
}
}

func (r *ResolverConfig) Pkg() *types.Package {
return types.NewPackage(r.ImportPath(), r.Dir())
}

func (r *ResolverConfig) IsDefined() bool {
switch r.Layout {
case LayoutSingleFile, "":
return r.Filename != ""
case LayoutFollowSchema:
return r.DirName != ""
default:
panic(fmt.Errorf("invalid layout %s", r.Layout))
}
}
5 changes: 5 additions & 0 deletions codegen/templates/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ type Options struct {
Filename string
RegionTags bool
GeneratedHeader bool
// PackageDoc is documentation written above the package line
PackageDoc string
// Data will be passed to the template execution.
Data interface{}
Funcs template.FuncMap
Expand Down Expand Up @@ -131,6 +133,9 @@ func Render(cfg Options) error {
if cfg.GeneratedHeader {
result.WriteString("// Code generated by github.com/99designs/gqlgen, DO NOT EDIT.\n\n")
}
if cfg.PackageDoc != "" {
result.WriteString(cfg.PackageDoc + "\n")
}
result.WriteString("package ")
result.WriteString(cfg.PackageName)
result.WriteString("\n\n")
Expand Down
Loading

0 comments on commit 9e3b399

Please sign in to comment.