Files
genqlient/generate/config.go
T

138 lines
4.4 KiB
Go

package generate
import (
"fmt"
"go/token"
"io/ioutil"
"path/filepath"
"gopkg.in/yaml.v2"
)
var defaultConfig = &Config{
Schema: "schema.graphql",
Operations: []string{"genqlient.graphql"},
Generated: "generated.go",
ContextType: "context.Context",
}
type Config struct {
// The filename with the GraphQL schema (in SDL format); defaults to
// schema.graphql
// TODO: Allow fetching a schema via introspection (will need to figure out
// how to convert that to SDL).
Schema string `yaml:"schema"`
// Filenames or globs with the operations for which to generate code;
// defaults to genqlient.graphql.
//
// These may be .graphql files, containing the queries in SDL format, or
// Go files, in which case any string-literal starting with (optional
// whitespace and) the string "# @genqlient" will be extracted as a query.
Operations []string `yaml:"operations"`
// If set, a file at this path will be generated containing the exact
// operations that genqlient will send to the server.
//
// This is useful for systems which require queries to be explicitly
// safelisted, especially for cases like queries involving fragments where
// it may not exactly match the input queries. The JSON is an object of
// the form
// {"operations": [{
// "operationName": "operationname",
// "query": "query operationName { ... }",
// "sourceLocation": "myqueriesfile.graphql",
// }]}
// Keys may be added in the future.
//
// By default, no such file is written.
ExportOperations string `yaml:"export_operations"`
// The filename to which to write the generated code; defaults to
// generated.go
Generated string `yaml:"generated"`
// The package name for the output code; defaults to the directory name of
// Generated
Package string `yaml:"package"`
// Set to the fully-qualified name of a type which generated helpers should
// accept and use as the context.Context for HTTP requests. Defaults to
// context.Context; set to the empty string to omit context entirely.
ContextType string `yaml:"context_type"`
// If set, a snippet of Go code to get a *graphql.Client from the context
// (which will be named ctx). For example, this might do
// ctx.Value(myKey).(*graphql.Client). If omitted, client must be
// passed to each method explicitly.
// TODO: what if you want to do an import in this snippet, e.g. for a
// getter function, global var, or a context-key-type?
// TODO: what if you want to return err?
ClientGetter string `yaml:"client_getter"`
// A map from GraphQL scalar type name to Go fully-qualified type name for
// the types to use for any custom or builtin scalars. By default, builtin
// scalars are mapped to the obvious Go types (String and ID to string, Int
// to int, Float to float64, and Boolean to bool), but this setting will
// extend or override those mappings. These types must define MarshalJSON
// and UnmarshalJSON methods, or otherwise be convertible to JSON.
Scalars map[string]string `yaml:"scalars"`
// Set automatically to the filename of the config file itself.
configFilename string
}
// baseDir returns the directory of the config-file (relative to which
// all the other paths are resolved).
func (c *Config) baseDir() string {
return filepath.Dir(c.configFilename)
}
func (c *Config) ValidateAndFillDefaults(configFilename string) error {
c.configFilename = configFilename
// Make paths relative to config dir
c.Schema = filepath.Join(c.baseDir(), c.Schema)
for i := range c.Operations {
c.Operations[i] = filepath.Join(c.baseDir(), c.Operations[i])
}
c.Generated = filepath.Join(c.baseDir(), c.Generated)
if c.Package == "" {
abs, err := filepath.Abs(c.Generated)
if err != nil {
return fmt.Errorf("unable to guess package-name: %v", err)
}
base := filepath.Base(filepath.Dir(abs))
if !token.IsIdentifier(base) {
return fmt.Errorf("unable to guess package-name: %v is not a valid identifier", base)
}
c.Package = base
}
return nil
}
func ReadAndValidateConfig(filename string) (*Config, error) {
config := *defaultConfig
if filename != "" {
text, err := ioutil.ReadFile(filename)
if err != nil {
return nil, fmt.Errorf("unreadable config file %v: %v", filename, err)
}
err = yaml.Unmarshal(text, &config)
if err != nil {
return nil, fmt.Errorf("invalid config file %v: %v", filename, err)
}
}
err := config.ValidateAndFillDefaults(filename)
if err != nil {
return nil, fmt.Errorf("invalid config file %v: %v", filename, err)
}
return &config, nil
}