diff --git a/generate/config.go b/generate/config.go index e96774d..43383dc 100644 --- a/generate/config.go +++ b/generate/config.go @@ -10,6 +10,10 @@ import ( "gopkg.in/yaml.v2" ) +// Config represents genqlient's configuration, generally read from +// genqlient.yaml. +// +// Callers must call ValidateAndFillDefaults before using the config. type Config struct { // The filename with the GraphQL schema (in SDL format); defaults to // schema.graphql @@ -100,8 +104,9 @@ type Config struct { // them at your own risk! AllowBrokenFeatures bool `yaml:"allow_broken_features"` - // Set automatically to the filename of the config file itself. - configFilename string + // The directory of the config-file (relative to which all the other paths + // are resolved). Set by ValidateAndFillDefaults. + baseDir string } // A TypeBinding represents a Go type to which genqlient will bind a particular @@ -134,22 +139,21 @@ type TypeBinding struct { ExpectExactFields string `yaml:"expect_exact_fields"` } -// 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 +// ValidateAndFillDefaults ensures that the configuration is valid, and fills +// in any options that were unspecified. +// +// The argument is the directory relative to which paths will be interpreted, +// typically the directory of the config file. +func (c *Config) ValidateAndFillDefaults(baseDir string) error { + c.baseDir = baseDir // Make paths relative to config dir - c.Schema = filepath.Join(c.baseDir(), c.Schema) + c.Schema = filepath.Join(baseDir, c.Schema) for i := range c.Operations { - c.Operations[i] = filepath.Join(c.baseDir(), c.Operations[i]) + c.Operations[i] = filepath.Join(baseDir, c.Operations[i]) } - c.Generated = filepath.Join(c.baseDir(), c.Generated) + c.Generated = filepath.Join(baseDir, c.Generated) if c.ExportOperations != "" { - c.ExportOperations = filepath.Join(c.baseDir(), c.ExportOperations) + c.ExportOperations = filepath.Join(baseDir, c.ExportOperations) } if c.ContextType == "" { @@ -173,6 +177,8 @@ func (c *Config) ValidateAndFillDefaults(configFilename string) error { return nil } +// ReadAndValidateConfig reads the configuration from the given file, validates +// it, and returns it. func ReadAndValidateConfig(filename string) (*Config, error) { text, err := ioutil.ReadFile(filename) if err != nil { @@ -185,7 +191,7 @@ func ReadAndValidateConfig(filename string) (*Config, error) { return nil, errorf(nil, "invalid config file %v: %v", filename, err) } - err = config.ValidateAndFillDefaults(filename) + err = config.ValidateAndFillDefaults(filepath.Dir(filename)) if err != nil { return nil, errorf(nil, "invalid config file %v: %v", filename, err) } diff --git a/generate/generate.go b/generate/generate.go index 8c9bc7c..986e92a 100644 --- a/generate/generate.go +++ b/generate/generate.go @@ -329,11 +329,12 @@ func (g *generator) addOperation(op *ast.OperationDefinition) error { return nil } -// Generate returns a map from absolute-path filename to generated content. +// Generate is the main programmatic entrypoint to genqlient, and generates and +// returns Go source code based on the given configuration. // -// This is the main entrypoint to the code-generation process for callers who -// wish to manage the config-reading (ReadAndValidateConfig) and file-writing -// on their own. (Those are wired in by Main.) +// See Config for more on creating a configuration. The return value is a map +// from filename to the generated file-content (e.g. Go source). Callers who +// don't want to manage reading and writing the files should call Main. func Generate(config *Config) (map[string][]byte, error) { // Step 1: Read in the schema and operations from the files defined by the // config (and validate the operations against the schema). This is all @@ -343,7 +344,7 @@ func Generate(config *Config) (map[string][]byte, error) { return nil, err } - document, err := getAndValidateQueries(config.baseDir(), config.Operations, schema) + document, err := getAndValidateQueries(config.baseDir, config.Operations, schema) if err != nil { return nil, err } diff --git a/generate/generate_test.go b/generate/generate_test.go index 6a271b2..566eb32 100644 --- a/generate/generate_test.go +++ b/generate/generate_test.go @@ -145,43 +145,43 @@ func defaultConfig(t *testing.T) *Config { // configurations. It uses snapshots, just like TestGenerate. func TestGenerateWithConfig(t *testing.T) { tests := []struct { - name string - fakeConfigFilename string - config *Config // omits Schema and Operations, set below. + name string + baseDir string // relative to dataDir + config *Config // omits Schema and Operations, set below. }{ - {"DefaultConfig", "genqlient.yaml", defaultConfig(t)}, - {"Subpackage", "genqlient.yaml", &Config{ + {"DefaultConfig", "", defaultConfig(t)}, + {"Subpackage", "", &Config{ Generated: "mypkg/myfile.go", }}, - {"SubpackageConfig", "mypkg/genqlient.yaml", &Config{ + {"SubpackageConfig", "mypkg", &Config{ Generated: "myfile.go", // (relative to genqlient.yaml) }}, - {"PackageName", "genqlient.yaml", &Config{ + {"PackageName", "", &Config{ Generated: "myfile.go", Package: "mypkg", }}, - {"ExportOperations", "genqlient.yaml", &Config{ + {"ExportOperations", "", &Config{ Generated: "generated.go", ExportOperations: "operations.json", }}, - {"CustomContext", "genqlient.yaml", &Config{ + {"CustomContext", "", &Config{ Generated: "generated.go", ContextType: "github.com/Khan/genqlient/internal/testutil.MyContext", }}, - {"NoContext", "genqlient.yaml", &Config{ + {"NoContext", "", &Config{ Generated: "generated.go", ContextType: "-", }}, - {"ClientGetter", "genqlient.yaml", &Config{ + {"ClientGetter", "", &Config{ Generated: "generated.go", ClientGetter: "github.com/Khan/genqlient/internal/testutil.GetClientFromContext", }}, - {"ClientGetterCustomContext", "genqlient.yaml", &Config{ + {"ClientGetterCustomContext", "", &Config{ Generated: "generated.go", ClientGetter: "github.com/Khan/genqlient/internal/testutil.GetClientFromMyContext", ContextType: "github.com/Khan/genqlient/internal/testutil.MyContext", }}, - {"ClientGetterNoContext", "genqlient.yaml", &Config{ + {"ClientGetterNoContext", "", &Config{ Generated: "generated.go", ClientGetter: "github.com/Khan/genqlient/internal/testutil.GetClientFromNowhere", ContextType: "-", @@ -192,9 +192,9 @@ func TestGenerateWithConfig(t *testing.T) { for _, test := range tests { config := test.config + baseDir := filepath.Join(dataDir, test.baseDir) t.Run(test.name, func(t *testing.T) { - err := config.ValidateAndFillDefaults( - filepath.Join(dataDir, test.fakeConfigFilename)) + err := config.ValidateAndFillDefaults(baseDir) config.Schema = filepath.Join(dataDir, "schema.graphql") config.Operations = []string{filepath.Join(dataDir, sourceFilename)} if err != nil { diff --git a/generate/main.go b/generate/main.go index 8841879..d35a097 100644 --- a/generate/main.go +++ b/generate/main.go @@ -1,3 +1,6 @@ +// Package generate provides programmatic access to genqlient's functionality, +// and documentation of its configuration options. For general usage +// documentation, see github.com/Khan/genqlient. package generate import ( @@ -50,6 +53,9 @@ See https://github.com/Khan/genqlient for full documentation. `) } +// Main is the command-line entrypoint to genqlient; it's equivalent to calling +// `go run github.com/Khan/genqlient`. For lower-level control over +// genqlient's operation, see Generate. func Main() { exitIfError := func(err error) { if err != nil { diff --git a/main.go b/main.go index d086fd8..5327d16 100644 --- a/main.go +++ b/main.go @@ -1,4 +1,9 @@ -// package main is at the root to allow "go run github.com/Khan/genqlient" +// genqlient is a GraphQL client generator for Go. +// +// To run genqlient: +// go run github.com/Khan/genqlient +// For programmatic access, see the "generate" package, below. For +// user documentation, see github.com/Khan/genqlient. package main import (