github.com/matiasanaya/gqlgen@v0.6.0/codegen/config.go (about)

     1  package codegen
     2  
     3  import (
     4  	"fmt"
     5  	"go/build"
     6  	"io/ioutil"
     7  	"os"
     8  	"path/filepath"
     9  	"strings"
    10  
    11  	"github.com/99designs/gqlgen/internal/gopath"
    12  	"github.com/pkg/errors"
    13  	"github.com/vektah/gqlparser/ast"
    14  	"gopkg.in/yaml.v2"
    15  )
    16  
    17  var cfgFilenames = []string{".gqlgen.yml", "gqlgen.yml", "gqlgen.yaml"}
    18  
    19  // DefaultConfig creates a copy of the default config
    20  func DefaultConfig() *Config {
    21  	return &Config{
    22  		SchemaFilename: "schema.graphql",
    23  		Model:          PackageConfig{Filename: "models_gen.go"},
    24  		Exec:           PackageConfig{Filename: "generated.go"},
    25  	}
    26  }
    27  
    28  // LoadConfigFromDefaultLocations looks for a config file in the current directory, and all parent directories
    29  // walking up the tree. The closest config file will be returned.
    30  func LoadConfigFromDefaultLocations() (*Config, error) {
    31  	cfgFile, err := findCfg()
    32  	if err != nil {
    33  		return nil, err
    34  	}
    35  
    36  	err = os.Chdir(filepath.Dir(cfgFile))
    37  	if err != nil {
    38  		return nil, errors.Wrap(err, "unable to enter config dir")
    39  	}
    40  	return LoadConfig(cfgFile)
    41  }
    42  
    43  // LoadConfig reads the gqlgen.yml config file
    44  func LoadConfig(filename string) (*Config, error) {
    45  	config := DefaultConfig()
    46  
    47  	b, err := ioutil.ReadFile(filename)
    48  	if err != nil {
    49  		return nil, errors.Wrap(err, "unable to read config")
    50  	}
    51  
    52  	if err := yaml.UnmarshalStrict(b, config); err != nil {
    53  		return nil, errors.Wrap(err, "unable to parse config")
    54  	}
    55  
    56  	config.FilePath = filename
    57  
    58  	return config, nil
    59  }
    60  
    61  type Config struct {
    62  	SchemaFilename string        `yaml:"schema,omitempty"`
    63  	SchemaStr      string        `yaml:"-"`
    64  	Exec           PackageConfig `yaml:"exec"`
    65  	Model          PackageConfig `yaml:"model"`
    66  	Resolver       PackageConfig `yaml:"resolver,omitempty"`
    67  	Models         TypeMap       `yaml:"models,omitempty"`
    68  	StructTag      string        `yaml:"struct_tag,omitempty"`
    69  
    70  	FilePath string `yaml:"-"`
    71  
    72  	schema *ast.Schema `yaml:"-"`
    73  }
    74  
    75  type PackageConfig struct {
    76  	Filename string `yaml:"filename,omitempty"`
    77  	Package  string `yaml:"package,omitempty"`
    78  	Type     string `yaml:"type,omitempty"`
    79  }
    80  
    81  type TypeMapEntry struct {
    82  	Model  string                  `yaml:"model"`
    83  	Fields map[string]TypeMapField `yaml:"fields,omitempty"`
    84  }
    85  
    86  type TypeMapField struct {
    87  	Resolver   bool   `yaml:"resolver"`
    88  	FieldName  string `yaml:"fieldName"`
    89  }
    90  
    91  func (c *PackageConfig) normalize() error {
    92  	if c.Filename == "" {
    93  		return errors.New("Filename is required")
    94  	}
    95  	c.Filename = abs(c.Filename)
    96  	// If Package is not set, first attempt to load the package at the output dir. If that fails
    97  	// fallback to just the base dir name of the output filename.
    98  	if c.Package == "" {
    99  		cwd, _ := os.Getwd()
   100  		pkg, _ := build.Default.Import(c.ImportPath(), cwd, 0)
   101  		if pkg.Name != "" {
   102  			c.Package = pkg.Name
   103  		} else {
   104  			c.Package = filepath.Base(c.Dir())
   105  		}
   106  	}
   107  	c.Package = sanitizePackageName(c.Package)
   108  	return nil
   109  }
   110  
   111  func (c *PackageConfig) ImportPath() string {
   112  	return gopath.MustDir2Import(c.Dir())
   113  }
   114  
   115  func (c *PackageConfig) Dir() string {
   116  	return filepath.Dir(c.Filename)
   117  }
   118  
   119  func (c *PackageConfig) Check() error {
   120  	if strings.ContainsAny(c.Package, "./\\") {
   121  		return fmt.Errorf("package should be the output package name only, do not include the output filename")
   122  	}
   123  	if c.Filename != "" && !strings.HasSuffix(c.Filename, ".go") {
   124  		return fmt.Errorf("filename should be path to a go source file")
   125  	}
   126  	return nil
   127  }
   128  
   129  func (c *PackageConfig) IsDefined() bool {
   130  	return c.Filename != ""
   131  }
   132  
   133  func (cfg *Config) Check() error {
   134  	if err := cfg.Models.Check(); err != nil {
   135  		return errors.Wrap(err, "config.models")
   136  	}
   137  	if err := cfg.Exec.Check(); err != nil {
   138  		return errors.Wrap(err, "config.exec")
   139  	}
   140  	if err := cfg.Model.Check(); err != nil {
   141  		return errors.Wrap(err, "config.model")
   142  	}
   143  	if err := cfg.Resolver.Check(); err != nil {
   144  		return errors.Wrap(err, "config.resolver")
   145  	}
   146  	return nil
   147  }
   148  
   149  type TypeMap map[string]TypeMapEntry
   150  
   151  func (tm TypeMap) Exists(typeName string) bool {
   152  	_, ok := tm[typeName]
   153  	return ok
   154  }
   155  
   156  func (tm TypeMap) Check() error {
   157  	for typeName, entry := range tm {
   158  		if strings.LastIndex(entry.Model, ".") < strings.LastIndex(entry.Model, "/") {
   159  			return fmt.Errorf("model %s: invalid type specifier \"%s\" - you need to specify a struct to map to", typeName, entry.Model)
   160  		}
   161  	}
   162  	return nil
   163  }
   164  
   165  // findCfg searches for the config file in this directory and all parents up the tree
   166  // looking for the closest match
   167  func findCfg() (string, error) {
   168  	dir, err := os.Getwd()
   169  	if err != nil {
   170  		return "", errors.Wrap(err, "unable to get working dir to findCfg")
   171  	}
   172  
   173  	cfg := findCfgInDir(dir)
   174  
   175  	for cfg == "" && dir != filepath.Dir(dir) {
   176  		dir = filepath.Dir(dir)
   177  		cfg = findCfgInDir(dir)
   178  	}
   179  
   180  	if cfg == "" {
   181  		return "", os.ErrNotExist
   182  	}
   183  
   184  	return cfg, nil
   185  }
   186  
   187  func findCfgInDir(dir string) string {
   188  	for _, cfgName := range cfgFilenames {
   189  		path := filepath.Join(dir, cfgName)
   190  		if _, err := os.Stat(path); err == nil {
   191  			return path
   192  		}
   193  	}
   194  	return ""
   195  }