aboutsummaryrefslogblamecommitdiffstats
path: root/vendor/github.com/99designs/gqlgen/codegen/config.go
blob: db0e467b78c5f5dc6577d02af870d39c8045f09d (plain) (tree)
1
2
3
4
5
6
7
8
9
10









                       
                                                     
                               
                                         


                          

                                                                       









                                                                                                              
                                                                 
                                                        
                                 

                               










                                                                          
                                 





                                                                     
                                                               


                                                                      


                                  






                                                              
                                                                
                                                              


                                                                  
 
                                     




                                                   
                                               







                                                                

                                            






















                                                                                                    
                                             


                                      
                                       











                                                                                                                       



                                          









                                                        


                                                          

































                                                                                                                                                    



                                         











                                                        
package codegen

import (
	"fmt"
	"go/build"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/99designs/gqlgen/internal/gopath"
	"github.com/pkg/errors"
	"github.com/vektah/gqlparser/ast"
	"gopkg.in/yaml.v2"
)

var cfgFilenames = []string{".gqlgen.yml", "gqlgen.yml", "gqlgen.yaml"}

// DefaultConfig creates a copy of the default config
func DefaultConfig() *Config {
	return &Config{
		SchemaFilename: "schema.graphql",
		Model:          PackageConfig{Filename: "models_gen.go"},
		Exec:           PackageConfig{Filename: "generated.go"},
	}
}

// LoadConfigFromDefaultLocations looks for a config file in the current directory, and all parent directories
// walking up the tree. The closest config file will be returned.
func LoadConfigFromDefaultLocations() (*Config, error) {
	cfgFile, err := findCfg()
	if err != nil {
		return nil, err
	}

	err = os.Chdir(filepath.Dir(cfgFile))
	if err != nil {
		return nil, errors.Wrap(err, "unable to enter config dir")
	}
	return LoadConfig(cfgFile)
}

// LoadConfig reads the gqlgen.yml config file
func LoadConfig(filename string) (*Config, error) {
	config := DefaultConfig()

	b, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, errors.Wrap(err, "unable to read config")
	}

	if err := yaml.UnmarshalStrict(b, config); err != nil {
		return nil, errors.Wrap(err, "unable to parse config")
	}

	config.FilePath = filename

	return config, nil
}

type Config struct {
	SchemaFilename string        `yaml:"schema,omitempty"`
	SchemaStr      string        `yaml:"-"`
	Exec           PackageConfig `yaml:"exec"`
	Model          PackageConfig `yaml:"model"`
	Resolver       PackageConfig `yaml:"resolver,omitempty"`
	Models         TypeMap       `yaml:"models,omitempty"`
	StructTag      string        `yaml:"struct_tag,omitempty"`

	FilePath string `yaml:"-"`

	schema *ast.Schema `yaml:"-"`
}

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

type TypeMapEntry struct {
	Model  string                  `yaml:"model"`
	Fields map[string]TypeMapField `yaml:"fields,omitempty"`
}

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

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 == "" {
		cwd, _ := os.Getwd()
		pkg, _ := build.Default.Import(c.ImportPath(), cwd, 0)
		if pkg.Name != "" {
			c.Package = pkg.Name
		} else {
			c.Package = filepath.Base(c.Dir())
		}
	}
	c.Package = sanitizePackageName(c.Package)
	return nil
}

func (c *PackageConfig) ImportPath() string {
	return gopath.MustDir2Import(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 nil
}

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

func (cfg *Config) Check() error {
	if err := cfg.Models.Check(); err != nil {
		return errors.Wrap(err, "config.models")
	}
	if err := cfg.Exec.Check(); err != nil {
		return errors.Wrap(err, "config.exec")
	}
	if err := cfg.Model.Check(); err != nil {
		return errors.Wrap(err, "config.model")
	}
	if err := cfg.Resolver.Check(); err != nil {
		return errors.Wrap(err, "config.resolver")
	}
	return nil
}

type TypeMap map[string]TypeMapEntry

func (tm TypeMap) Exists(typeName string) bool {
	_, ok := tm[typeName]
	return ok
}

func (tm TypeMap) Check() error {
	for typeName, entry := range tm {
		if strings.LastIndex(entry.Model, ".") < strings.LastIndex(entry.Model, "/") {
			return fmt.Errorf("model %s: invalid type specifier \"%s\" - you need to specify a struct to map to", typeName, entry.Model)
		}
	}
	return nil
}

// findCfg searches for the config file in this directory and all parents up the tree
// looking for the closest match
func findCfg() (string, error) {
	dir, err := os.Getwd()
	if err != nil {
		return "", errors.Wrap(err, "unable to get working dir to findCfg")
	}

	cfg := findCfgInDir(dir)

	for cfg == "" && dir != filepath.Dir(dir) {
		dir = filepath.Dir(dir)
		cfg = findCfgInDir(dir)
	}

	if cfg == "" {
		return "", os.ErrNotExist
	}

	return cfg, nil
}

func findCfgInDir(dir string) string {
	for _, cfgName := range cfgFilenames {
		path := filepath.Join(dir, cfgName)
		if _, err := os.Stat(path); err == nil {
			return path
		}
	}
	return ""
}