...

Source file src/github.com/99designs/gqlgen/codegen/config/config.go

Documentation: github.com/99designs/gqlgen/codegen/config

     1  package config
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"go/types"
     7  	"io"
     8  	"os"
     9  	"path/filepath"
    10  	"regexp"
    11  	"sort"
    12  	"strings"
    13  
    14  	"github.com/vektah/gqlparser/v2"
    15  	"github.com/vektah/gqlparser/v2/ast"
    16  	"golang.org/x/tools/go/packages"
    17  	"gopkg.in/yaml.v3"
    18  
    19  	"github.com/99designs/gqlgen/codegen/templates"
    20  	"github.com/99designs/gqlgen/internal/code"
    21  )
    22  
    23  type Config struct {
    24  	SchemaFilename                StringList                 `yaml:"schema,omitempty"`
    25  	Exec                          ExecConfig                 `yaml:"exec"`
    26  	Model                         PackageConfig              `yaml:"model,omitempty"`
    27  	Federation                    PackageConfig              `yaml:"federation,omitempty"`
    28  	Resolver                      ResolverConfig             `yaml:"resolver,omitempty"`
    29  	AutoBind                      []string                   `yaml:"autobind"`
    30  	Models                        TypeMap                    `yaml:"models,omitempty"`
    31  	StructTag                     string                     `yaml:"struct_tag,omitempty"`
    32  	Directives                    map[string]DirectiveConfig `yaml:"directives,omitempty"`
    33  	GoBuildTags                   StringList                 `yaml:"go_build_tags,omitempty"`
    34  	GoInitialisms                 GoInitialismsConfig        `yaml:"go_initialisms,omitempty"`
    35  	OmitSliceElementPointers      bool                       `yaml:"omit_slice_element_pointers,omitempty"`
    36  	OmitGetters                   bool                       `yaml:"omit_getters,omitempty"`
    37  	OmitInterfaceChecks           bool                       `yaml:"omit_interface_checks,omitempty"`
    38  	OmitComplexity                bool                       `yaml:"omit_complexity,omitempty"`
    39  	OmitGQLGenFileNotice          bool                       `yaml:"omit_gqlgen_file_notice,omitempty"`
    40  	OmitGQLGenVersionInFileNotice bool                       `yaml:"omit_gqlgen_version_in_file_notice,omitempty"`
    41  	OmitRootModels                bool                       `yaml:"omit_root_models,omitempty"`
    42  	OmitResolverFields            bool                       `yaml:"omit_resolver_fields,omitempty"`
    43  	StructFieldsAlwaysPointers    bool                       `yaml:"struct_fields_always_pointers,omitempty"`
    44  	ReturnPointersInUmarshalInput bool                       `yaml:"return_pointers_in_unmarshalinput,omitempty"`
    45  	ResolversAlwaysReturnPointers bool                       `yaml:"resolvers_always_return_pointers,omitempty"`
    46  	NullableInputOmittable        bool                       `yaml:"nullable_input_omittable,omitempty"`
    47  	EnableModelJsonOmitemptyTag   *bool                      `yaml:"enable_model_json_omitempty_tag,omitempty"`
    48  	SkipValidation                bool                       `yaml:"skip_validation,omitempty"`
    49  	SkipModTidy                   bool                       `yaml:"skip_mod_tidy,omitempty"`
    50  	Sources                       []*ast.Source              `yaml:"-"`
    51  	Packages                      *code.Packages             `yaml:"-"`
    52  	Schema                        *ast.Schema                `yaml:"-"`
    53  
    54  	// Deprecated: use Federation instead. Will be removed next release
    55  	Federated bool `yaml:"federated,omitempty"`
    56  }
    57  
    58  var cfgFilenames = []string{".gqlgen.yml", "gqlgen.yml", "gqlgen.yaml"}
    59  
    60  // DefaultConfig creates a copy of the default config
    61  func DefaultConfig() *Config {
    62  	return &Config{
    63  		SchemaFilename:                StringList{"schema.graphql"},
    64  		Model:                         PackageConfig{Filename: "models_gen.go"},
    65  		Exec:                          ExecConfig{Filename: "generated.go"},
    66  		Directives:                    map[string]DirectiveConfig{},
    67  		Models:                        TypeMap{},
    68  		StructFieldsAlwaysPointers:    true,
    69  		ReturnPointersInUmarshalInput: false,
    70  		ResolversAlwaysReturnPointers: true,
    71  		NullableInputOmittable:        false,
    72  	}
    73  }
    74  
    75  // LoadDefaultConfig loads the default config so that it is ready to be used
    76  func LoadDefaultConfig() (*Config, error) {
    77  	config := DefaultConfig()
    78  
    79  	for _, filename := range config.SchemaFilename {
    80  		filename = filepath.ToSlash(filename)
    81  		var err error
    82  		var schemaRaw []byte
    83  		schemaRaw, err = os.ReadFile(filename)
    84  		if err != nil {
    85  			return nil, fmt.Errorf("unable to open schema: %w", err)
    86  		}
    87  
    88  		config.Sources = append(config.Sources, &ast.Source{Name: filename, Input: string(schemaRaw)})
    89  	}
    90  
    91  	return config, nil
    92  }
    93  
    94  // LoadConfigFromDefaultLocations looks for a config file in the current directory, and all parent directories
    95  // walking up the tree. The closest config file will be returned.
    96  func LoadConfigFromDefaultLocations() (*Config, error) {
    97  	cfgFile, err := findCfg()
    98  	if err != nil {
    99  		return nil, err
   100  	}
   101  
   102  	err = os.Chdir(filepath.Dir(cfgFile))
   103  	if err != nil {
   104  		return nil, fmt.Errorf("unable to enter config dir: %w", err)
   105  	}
   106  	return LoadConfig(cfgFile)
   107  }
   108  
   109  var path2regex = strings.NewReplacer(
   110  	`.`, `\.`,
   111  	`*`, `.+`,
   112  	`\`, `[\\/]`,
   113  	`/`, `[\\/]`,
   114  )
   115  
   116  // LoadConfig reads the gqlgen.yml config file
   117  func LoadConfig(filename string) (*Config, error) {
   118  	b, err := os.ReadFile(filename)
   119  	if err != nil {
   120  		return nil, fmt.Errorf("unable to read config: %w", err)
   121  	}
   122  
   123  	return ReadConfig(bytes.NewReader(b))
   124  }
   125  
   126  func ReadConfig(cfgFile io.Reader) (*Config, error) {
   127  	config := DefaultConfig()
   128  
   129  	dec := yaml.NewDecoder(cfgFile)
   130  	dec.KnownFields(true)
   131  
   132  	if err := dec.Decode(config); err != nil {
   133  		return nil, fmt.Errorf("unable to parse config: %w", err)
   134  	}
   135  
   136  	if err := CompleteConfig(config); err != nil {
   137  		return nil, err
   138  	}
   139  
   140  	return config, nil
   141  }
   142  
   143  // CompleteConfig fills in the schema and other values to a config loaded from
   144  // YAML.
   145  func CompleteConfig(config *Config) error {
   146  	defaultDirectives := map[string]DirectiveConfig{
   147  		"skip":        {SkipRuntime: true},
   148  		"include":     {SkipRuntime: true},
   149  		"deprecated":  {SkipRuntime: true},
   150  		"specifiedBy": {SkipRuntime: true},
   151  	}
   152  
   153  	for key, value := range defaultDirectives {
   154  		if _, defined := config.Directives[key]; !defined {
   155  			config.Directives[key] = value
   156  		}
   157  	}
   158  
   159  	preGlobbing := config.SchemaFilename
   160  	config.SchemaFilename = StringList{}
   161  	for _, f := range preGlobbing {
   162  		var matches []string
   163  
   164  		// for ** we want to override default globbing patterns and walk all
   165  		// subdirectories to match schema files.
   166  		if strings.Contains(f, "**") {
   167  			pathParts := strings.SplitN(f, "**", 2)
   168  			rest := strings.TrimPrefix(strings.TrimPrefix(pathParts[1], `\`), `/`)
   169  			// turn the rest of the glob into a regex, anchored only at the end because ** allows
   170  			// for any number of dirs in between and walk will let us match against the full path name
   171  			globRe := regexp.MustCompile(path2regex.Replace(rest) + `$`)
   172  
   173  			if err := filepath.Walk(pathParts[0], func(path string, info os.FileInfo, err error) error {
   174  				if err != nil {
   175  					return err
   176  				}
   177  
   178  				if globRe.MatchString(strings.TrimPrefix(path, pathParts[0])) {
   179  					matches = append(matches, path)
   180  				}
   181  
   182  				return nil
   183  			}); err != nil {
   184  				return fmt.Errorf("failed to walk schema at root %s: %w", pathParts[0], err)
   185  			}
   186  		} else {
   187  			var err error
   188  			matches, err = filepath.Glob(f)
   189  			if err != nil {
   190  				return fmt.Errorf("failed to glob schema filename %s: %w", f, err)
   191  			}
   192  		}
   193  
   194  		for _, m := range matches {
   195  			if config.SchemaFilename.Has(m) {
   196  				continue
   197  			}
   198  			config.SchemaFilename = append(config.SchemaFilename, m)
   199  		}
   200  	}
   201  
   202  	for _, filename := range config.SchemaFilename {
   203  		filename = filepath.ToSlash(filename)
   204  		var err error
   205  		var schemaRaw []byte
   206  		schemaRaw, err = os.ReadFile(filename)
   207  		if err != nil {
   208  			return fmt.Errorf("unable to open schema: %w", err)
   209  		}
   210  
   211  		config.Sources = append(config.Sources, &ast.Source{Name: filename, Input: string(schemaRaw)})
   212  	}
   213  
   214  	config.GoInitialisms.setInitialisms()
   215  
   216  	return nil
   217  }
   218  
   219  func (c *Config) Init() error {
   220  	if c.Packages == nil {
   221  		c.Packages = code.NewPackages(
   222  			code.WithBuildTags(c.GoBuildTags...),
   223  		)
   224  	}
   225  
   226  	if c.Schema == nil {
   227  		if err := c.LoadSchema(); err != nil {
   228  			return err
   229  		}
   230  	}
   231  
   232  	err := c.injectTypesFromSchema()
   233  	if err != nil {
   234  		return err
   235  	}
   236  
   237  	err = c.autobind()
   238  	if err != nil {
   239  		return err
   240  	}
   241  
   242  	c.injectBuiltins()
   243  	// prefetch all packages in one big packages.Load call
   244  	c.Packages.LoadAll(c.packageList()...)
   245  
   246  	//  check everything is valid on the way out
   247  	err = c.check()
   248  	if err != nil {
   249  		return err
   250  	}
   251  
   252  	return nil
   253  }
   254  
   255  func (c *Config) packageList() []string {
   256  	pkgs := []string{
   257  		"github.com/99designs/gqlgen/graphql",
   258  		"github.com/99designs/gqlgen/graphql/introspection",
   259  	}
   260  	pkgs = append(pkgs, c.Models.ReferencedPackages()...)
   261  	pkgs = append(pkgs, c.AutoBind...)
   262  	return pkgs
   263  }
   264  
   265  func (c *Config) ReloadAllPackages() {
   266  	c.Packages.ReloadAll(c.packageList()...)
   267  }
   268  
   269  func (c *Config) IsRoot(def *ast.Definition) bool {
   270  	return def == c.Schema.Query || def == c.Schema.Mutation || def == c.Schema.Subscription
   271  }
   272  
   273  func (c *Config) injectTypesFromSchema() error {
   274  	c.Directives["goModel"] = DirectiveConfig{
   275  		SkipRuntime: true,
   276  	}
   277  
   278  	c.Directives["goField"] = DirectiveConfig{
   279  		SkipRuntime: true,
   280  	}
   281  
   282  	c.Directives["goTag"] = DirectiveConfig{
   283  		SkipRuntime: true,
   284  	}
   285  
   286  	for _, schemaType := range c.Schema.Types {
   287  		if c.IsRoot(schemaType) {
   288  			continue
   289  		}
   290  
   291  		if bd := schemaType.Directives.ForName("goModel"); bd != nil {
   292  			if ma := bd.Arguments.ForName("model"); ma != nil {
   293  				if mv, err := ma.Value.Value(nil); err == nil {
   294  					c.Models.Add(schemaType.Name, mv.(string))
   295  				}
   296  			}
   297  
   298  			if ma := bd.Arguments.ForName("models"); ma != nil {
   299  				if mvs, err := ma.Value.Value(nil); err == nil {
   300  					for _, mv := range mvs.([]interface{}) {
   301  						c.Models.Add(schemaType.Name, mv.(string))
   302  					}
   303  				}
   304  			}
   305  
   306  			if fg := bd.Arguments.ForName("forceGenerate"); fg != nil {
   307  				if mv, err := fg.Value.Value(nil); err == nil {
   308  					c.Models.ForceGenerate(schemaType.Name, mv.(bool))
   309  				}
   310  			}
   311  		}
   312  
   313  		if schemaType.Kind == ast.Object || schemaType.Kind == ast.InputObject {
   314  			for _, field := range schemaType.Fields {
   315  				if fd := field.Directives.ForName("goField"); fd != nil {
   316  					forceResolver := c.Models[schemaType.Name].Fields[field.Name].Resolver
   317  					fieldName := c.Models[schemaType.Name].Fields[field.Name].FieldName
   318  
   319  					if ra := fd.Arguments.ForName("forceResolver"); ra != nil {
   320  						if fr, err := ra.Value.Value(nil); err == nil {
   321  							forceResolver = fr.(bool)
   322  						}
   323  					}
   324  
   325  					if na := fd.Arguments.ForName("name"); na != nil {
   326  						if fr, err := na.Value.Value(nil); err == nil {
   327  							fieldName = fr.(string)
   328  						}
   329  					}
   330  
   331  					if c.Models[schemaType.Name].Fields == nil {
   332  						c.Models[schemaType.Name] = TypeMapEntry{
   333  							Model:       c.Models[schemaType.Name].Model,
   334  							ExtraFields: c.Models[schemaType.Name].ExtraFields,
   335  							Fields:      map[string]TypeMapField{},
   336  						}
   337  					}
   338  
   339  					c.Models[schemaType.Name].Fields[field.Name] = TypeMapField{
   340  						FieldName: fieldName,
   341  						Resolver:  forceResolver,
   342  					}
   343  				}
   344  			}
   345  		}
   346  	}
   347  
   348  	return nil
   349  }
   350  
   351  type TypeMapEntry struct {
   352  	Model         StringList              `yaml:"model,omitempty"`
   353  	ForceGenerate bool                    `yaml:"forceGenerate,omitempty"`
   354  	Fields        map[string]TypeMapField `yaml:"fields,omitempty"`
   355  
   356  	// Key is the Go name of the field.
   357  	ExtraFields map[string]ModelExtraField `yaml:"extraFields,omitempty"`
   358  }
   359  
   360  type TypeMapField struct {
   361  	Resolver        bool   `yaml:"resolver"`
   362  	FieldName       string `yaml:"fieldName"`
   363  	GeneratedMethod string `yaml:"-"`
   364  }
   365  
   366  type ModelExtraField struct {
   367  	// Type is the Go type of the field.
   368  	//
   369  	// It supports the builtin basic types (like string or int64), named types
   370  	// (qualified by the full package path), pointers to those types (prefixed
   371  	// with `*`), and slices of those types (prefixed with `[]`).
   372  	//
   373  	// For example, the following are valid types:
   374  	//  string
   375  	//  *github.com/author/package.Type
   376  	//  []string
   377  	//  []*github.com/author/package.Type
   378  	//
   379  	// Note that the type will be referenced from the generated/graphql, which
   380  	// means the package it lives in must not reference the generated/graphql
   381  	// package to avoid circular imports.
   382  	// restrictions.
   383  	Type string `yaml:"type"`
   384  
   385  	// OverrideTags is an optional override of the Go field tag.
   386  	OverrideTags string `yaml:"overrideTags"`
   387  
   388  	// Description is an optional the Go field doc-comment.
   389  	Description string `yaml:"description"`
   390  }
   391  
   392  type StringList []string
   393  
   394  func (a *StringList) UnmarshalYAML(unmarshal func(interface{}) error) error {
   395  	var single string
   396  	err := unmarshal(&single)
   397  	if err == nil {
   398  		*a = []string{single}
   399  		return nil
   400  	}
   401  
   402  	var multi []string
   403  	err = unmarshal(&multi)
   404  	if err != nil {
   405  		return err
   406  	}
   407  
   408  	*a = multi
   409  	return nil
   410  }
   411  
   412  func (a StringList) Has(file string) bool {
   413  	for _, existing := range a {
   414  		if existing == file {
   415  			return true
   416  		}
   417  	}
   418  	return false
   419  }
   420  
   421  func (c *Config) check() error {
   422  	if c.Models == nil {
   423  		c.Models = TypeMap{}
   424  	}
   425  
   426  	type FilenamePackage struct {
   427  		Filename string
   428  		Package  string
   429  		Declaree string
   430  	}
   431  
   432  	fileList := map[string][]FilenamePackage{}
   433  
   434  	if err := c.Models.Check(); err != nil {
   435  		return fmt.Errorf("config.models: %w", err)
   436  	}
   437  	if err := c.Exec.Check(); err != nil {
   438  		return fmt.Errorf("config.exec: %w", err)
   439  	}
   440  	fileList[c.Exec.ImportPath()] = append(fileList[c.Exec.ImportPath()], FilenamePackage{
   441  		Filename: c.Exec.Filename,
   442  		Package:  c.Exec.Package,
   443  		Declaree: "exec",
   444  	})
   445  
   446  	if c.Model.IsDefined() {
   447  		if err := c.Model.Check(); err != nil {
   448  			return fmt.Errorf("config.model: %w", err)
   449  		}
   450  		fileList[c.Model.ImportPath()] = append(fileList[c.Model.ImportPath()], FilenamePackage{
   451  			Filename: c.Model.Filename,
   452  			Package:  c.Model.Package,
   453  			Declaree: "model",
   454  		})
   455  	}
   456  	if c.Resolver.IsDefined() {
   457  		if err := c.Resolver.Check(); err != nil {
   458  			return fmt.Errorf("config.resolver: %w", err)
   459  		}
   460  		fileList[c.Resolver.ImportPath()] = append(fileList[c.Resolver.ImportPath()], FilenamePackage{
   461  			Filename: c.Resolver.Filename,
   462  			Package:  c.Resolver.Package,
   463  			Declaree: "resolver",
   464  		})
   465  	}
   466  	if c.Federation.IsDefined() {
   467  		if err := c.Federation.Check(); err != nil {
   468  			return fmt.Errorf("config.federation: %w", err)
   469  		}
   470  		fileList[c.Federation.ImportPath()] = append(fileList[c.Federation.ImportPath()], FilenamePackage{
   471  			Filename: c.Federation.Filename,
   472  			Package:  c.Federation.Package,
   473  			Declaree: "federation",
   474  		})
   475  		if c.Federation.ImportPath() != c.Exec.ImportPath() {
   476  			return fmt.Errorf("federation and exec must be in the same package")
   477  		}
   478  	}
   479  	if c.Federated {
   480  		return fmt.Errorf("federated has been removed, instead use\nfederation:\n    filename: path/to/federated.go")
   481  	}
   482  
   483  	for importPath, pkg := range fileList {
   484  		for _, file1 := range pkg {
   485  			for _, file2 := range pkg {
   486  				if file1.Package != file2.Package {
   487  					return fmt.Errorf("%s and %s define the same import path (%s) with different package names (%s vs %s)",
   488  						file1.Declaree,
   489  						file2.Declaree,
   490  						importPath,
   491  						file1.Package,
   492  						file2.Package,
   493  					)
   494  				}
   495  			}
   496  		}
   497  	}
   498  
   499  	return nil
   500  }
   501  
   502  type TypeMap map[string]TypeMapEntry
   503  
   504  func (tm TypeMap) Exists(typeName string) bool {
   505  	_, ok := tm[typeName]
   506  	return ok
   507  }
   508  
   509  func (tm TypeMap) UserDefined(typeName string) bool {
   510  	m, ok := tm[typeName]
   511  	return ok && len(m.Model) > 0
   512  }
   513  
   514  func (tm TypeMap) Check() error {
   515  	for typeName, entry := range tm {
   516  		for _, model := range entry.Model {
   517  			if strings.LastIndex(model, ".") < strings.LastIndex(model, "/") {
   518  				return fmt.Errorf("model %s: invalid type specifier \"%s\" - you need to specify a struct to map to", typeName, entry.Model)
   519  			}
   520  		}
   521  	}
   522  	return nil
   523  }
   524  
   525  func (tm TypeMap) ReferencedPackages() []string {
   526  	var pkgs []string
   527  
   528  	for _, typ := range tm {
   529  		for _, model := range typ.Model {
   530  			if model == "map[string]interface{}" || model == "interface{}" {
   531  				continue
   532  			}
   533  			pkg, _ := code.PkgAndType(model)
   534  			if pkg == "" || inStrSlice(pkgs, pkg) {
   535  				continue
   536  			}
   537  			pkgs = append(pkgs, code.QualifyPackagePath(pkg))
   538  		}
   539  	}
   540  
   541  	sort.Slice(pkgs, func(i, j int) bool {
   542  		return pkgs[i] > pkgs[j]
   543  	})
   544  	return pkgs
   545  }
   546  
   547  func (tm TypeMap) Add(name string, goType string) {
   548  	modelCfg := tm[name]
   549  	modelCfg.Model = append(modelCfg.Model, goType)
   550  	tm[name] = modelCfg
   551  }
   552  
   553  func (tm TypeMap) ForceGenerate(name string, forceGenerate bool) {
   554  	modelCfg := tm[name]
   555  	modelCfg.ForceGenerate = forceGenerate
   556  	tm[name] = modelCfg
   557  }
   558  
   559  type DirectiveConfig struct {
   560  	SkipRuntime bool `yaml:"skip_runtime"`
   561  }
   562  
   563  func inStrSlice(haystack []string, needle string) bool {
   564  	for _, v := range haystack {
   565  		if needle == v {
   566  			return true
   567  		}
   568  	}
   569  
   570  	return false
   571  }
   572  
   573  // findCfg searches for the config file in this directory and all parents up the tree
   574  // looking for the closest match
   575  func findCfg() (string, error) {
   576  	dir, err := os.Getwd()
   577  	if err != nil {
   578  		return "", fmt.Errorf("unable to get working dir to findCfg: %w", err)
   579  	}
   580  
   581  	cfg := findCfgInDir(dir)
   582  
   583  	for cfg == "" && dir != filepath.Dir(dir) {
   584  		dir = filepath.Dir(dir)
   585  		cfg = findCfgInDir(dir)
   586  	}
   587  
   588  	if cfg == "" {
   589  		return "", os.ErrNotExist
   590  	}
   591  
   592  	return cfg, nil
   593  }
   594  
   595  func findCfgInDir(dir string) string {
   596  	for _, cfgName := range cfgFilenames {
   597  		path := filepath.Join(dir, cfgName)
   598  		if _, err := os.Stat(path); err == nil {
   599  			return path
   600  		}
   601  	}
   602  	return ""
   603  }
   604  
   605  func (c *Config) autobind() error {
   606  	if len(c.AutoBind) == 0 {
   607  		return nil
   608  	}
   609  
   610  	ps := c.Packages.LoadAll(c.AutoBind...)
   611  
   612  	for _, t := range c.Schema.Types {
   613  		if c.Models.UserDefined(t.Name) || c.Models[t.Name].ForceGenerate {
   614  			continue
   615  		}
   616  
   617  		for i, p := range ps {
   618  			if p == nil || p.Module == nil {
   619  				return fmt.Errorf("unable to load %s - make sure you're using an import path to a package that exists", c.AutoBind[i])
   620  			}
   621  
   622  			autobindType := c.lookupAutobindType(p, t)
   623  			if autobindType != nil {
   624  				c.Models.Add(t.Name, autobindType.Pkg().Path()+"."+autobindType.Name())
   625  				break
   626  			}
   627  		}
   628  	}
   629  
   630  	for i, t := range c.Models {
   631  		if t.ForceGenerate {
   632  			continue
   633  		}
   634  
   635  		for j, m := range t.Model {
   636  			pkg, typename := code.PkgAndType(m)
   637  
   638  			// skip anything that looks like an import path
   639  			if strings.Contains(pkg, "/") {
   640  				continue
   641  			}
   642  
   643  			for _, p := range ps {
   644  				if p.Name != pkg {
   645  					continue
   646  				}
   647  				if t := p.Types.Scope().Lookup(typename); t != nil {
   648  					c.Models[i].Model[j] = t.Pkg().Path() + "." + t.Name()
   649  					break
   650  				}
   651  			}
   652  		}
   653  	}
   654  
   655  	return nil
   656  }
   657  
   658  func (c *Config) lookupAutobindType(p *packages.Package, schemaType *ast.Definition) types.Object {
   659  	// Try binding to either the original schema type name, or the normalized go type name
   660  	for _, lookupName := range []string{schemaType.Name, templates.ToGo(schemaType.Name)} {
   661  		if t := p.Types.Scope().Lookup(lookupName); t != nil {
   662  			return t
   663  		}
   664  	}
   665  
   666  	return nil
   667  }
   668  
   669  func (c *Config) injectBuiltins() {
   670  	builtins := TypeMap{
   671  		"__Directive":         {Model: StringList{"github.com/99designs/gqlgen/graphql/introspection.Directive"}},
   672  		"__DirectiveLocation": {Model: StringList{"github.com/99designs/gqlgen/graphql.String"}},
   673  		"__Type":              {Model: StringList{"github.com/99designs/gqlgen/graphql/introspection.Type"}},
   674  		"__TypeKind":          {Model: StringList{"github.com/99designs/gqlgen/graphql.String"}},
   675  		"__Field":             {Model: StringList{"github.com/99designs/gqlgen/graphql/introspection.Field"}},
   676  		"__EnumValue":         {Model: StringList{"github.com/99designs/gqlgen/graphql/introspection.EnumValue"}},
   677  		"__InputValue":        {Model: StringList{"github.com/99designs/gqlgen/graphql/introspection.InputValue"}},
   678  		"__Schema":            {Model: StringList{"github.com/99designs/gqlgen/graphql/introspection.Schema"}},
   679  		"Float":               {Model: StringList{"github.com/99designs/gqlgen/graphql.FloatContext"}},
   680  		"String":              {Model: StringList{"github.com/99designs/gqlgen/graphql.String"}},
   681  		"Boolean":             {Model: StringList{"github.com/99designs/gqlgen/graphql.Boolean"}},
   682  		"Int": {Model: StringList{
   683  			"github.com/99designs/gqlgen/graphql.Int",
   684  			"github.com/99designs/gqlgen/graphql.Int32",
   685  			"github.com/99designs/gqlgen/graphql.Int64",
   686  		}},
   687  		"ID": {
   688  			Model: StringList{
   689  				"github.com/99designs/gqlgen/graphql.ID",
   690  				"github.com/99designs/gqlgen/graphql.IntID",
   691  			},
   692  		},
   693  	}
   694  
   695  	for typeName, entry := range builtins {
   696  		if !c.Models.Exists(typeName) {
   697  			c.Models[typeName] = entry
   698  		}
   699  	}
   700  
   701  	// These are additional types that are injected if defined in the schema as scalars.
   702  	extraBuiltins := TypeMap{
   703  		"Time":   {Model: StringList{"github.com/99designs/gqlgen/graphql.Time"}},
   704  		"Map":    {Model: StringList{"github.com/99designs/gqlgen/graphql.Map"}},
   705  		"Upload": {Model: StringList{"github.com/99designs/gqlgen/graphql.Upload"}},
   706  		"Any":    {Model: StringList{"github.com/99designs/gqlgen/graphql.Any"}},
   707  	}
   708  
   709  	for typeName, entry := range extraBuiltins {
   710  		if t, ok := c.Schema.Types[typeName]; !c.Models.Exists(typeName) && ok && t.Kind == ast.Scalar {
   711  			c.Models[typeName] = entry
   712  		}
   713  	}
   714  }
   715  
   716  func (c *Config) LoadSchema() error {
   717  	if c.Packages != nil {
   718  		c.Packages = code.NewPackages(
   719  			code.WithBuildTags(c.GoBuildTags...),
   720  		)
   721  	}
   722  
   723  	if err := c.check(); err != nil {
   724  		return err
   725  	}
   726  
   727  	schema, err := gqlparser.LoadSchema(c.Sources...)
   728  	if err != nil {
   729  		return err
   730  	}
   731  
   732  	if schema.Query == nil {
   733  		schema.Query = &ast.Definition{
   734  			Kind: ast.Object,
   735  			Name: "Query",
   736  		}
   737  		schema.Types["Query"] = schema.Query
   738  	}
   739  
   740  	c.Schema = schema
   741  	return nil
   742  }
   743  
   744  func abs(path string) string {
   745  	absPath, err := filepath.Abs(path)
   746  	if err != nil {
   747  		panic(err)
   748  	}
   749  	return filepath.ToSlash(absPath)
   750  }
   751  

View as plain text