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 }