diff --git a/example/genqlient.yaml b/example/genqlient.yaml index 8792985..40236c1 100644 --- a/example/genqlient.yaml +++ b/example/genqlient.yaml @@ -1,6 +1,7 @@ # These are the defaults, and are just included to be explicit. package: example schema: schema.graphql -queries: queries.graphql +queries: +- queries.graphql generated: generated.go use_context: true diff --git a/generate/config.go b/generate/config.go index 1a9cfe2..4cc2423 100644 --- a/generate/config.go +++ b/generate/config.go @@ -12,7 +12,7 @@ import ( var defaultConfig = &Config{ Schema: "schema.graphql", - Queries: "queries.graphql", + Queries: []string{"queries.graphql"}, Generated: "generated.go", ContextType: "context.Context", } @@ -26,9 +26,8 @@ type Config struct { // TODO: Allow fetching a schema via introspection (will need to figure out // how to convert that to SDL). Schema string `yaml:"schema"` - // The filename with the queries; defaults to queries.graphql - // TODO: allow multiple files? - Queries string `yaml:"queries"` + // Filenames or globs with the queries; defaults to queries.graphql. + Queries []string `yaml:"queries"` // The filename to which to write the generated code; defaults to // generated.go Generated string `yaml:"generated"` @@ -50,7 +49,9 @@ func (c *Config) ValidateAndFillDefaults(configFilename string) error { // Make paths relative to config dir configDir := filepath.Dir(configFilename) c.Schema = filepath.Join(configDir, c.Schema) - c.Queries = filepath.Join(configDir, c.Queries) + for i := range c.Queries { + c.Queries[i] = filepath.Join(configDir, c.Queries[i]) + } c.Generated = filepath.Join(configDir, c.Generated) if c.Package == "" { diff --git a/generate/generate_test.go b/generate/generate_test.go index 685dffd..60da5d2 100644 --- a/generate/generate_test.go +++ b/generate/generate_test.go @@ -57,7 +57,7 @@ func TestGenerate(t *testing.T) { goCode, err := Generate(&Config{ Schema: filepath.Join("testdata", "schema.graphql"), - Queries: filepath.Join("testdata", graphqlFilename), + Queries: []string{filepath.Join("testdata", graphqlFilename)}, Package: "test", }) if err != nil { diff --git a/generate/parse.go b/generate/parse.go index 0b0610a..a795f92 100644 --- a/generate/parse.go +++ b/generate/parse.go @@ -3,6 +3,7 @@ package generate import ( "fmt" "io/ioutil" + "path/filepath" "github.com/vektah/gqlparser/v2" "github.com/vektah/gqlparser/v2/ast" @@ -26,24 +27,50 @@ func getSchema(filename string) (*ast.Schema, error) { return schema, nil } -func getAndValidateQueries(filename string, schema *ast.Schema) (*ast.QueryDocument, error) { - text, err := ioutil.ReadFile(filename) - if err != nil { - return nil, fmt.Errorf("unreadable query-spec file %v: %v", filename, err) +func getAndValidateQueries(filenames []string, schema *ast.Schema) (*ast.QueryDocument, error) { + // We merge all the queries into a single query-document, since operations + // in one might reference fragments in another. + // + // TODO(benkraft): It might be better to merge just within a filename, so + // that fragment-names don't need to be unique across files. + mergedQueryDoc := new(ast.QueryDocument) + + for _, filename := range filenames { + switch filepath.Ext(filename) { + case ".graphql": + // Cf. gqlparser.LoadQuery + text, err := ioutil.ReadFile(filename) + if err != nil { + return nil, fmt.Errorf("unreadable query-spec file %v: %v", filename, err) + } + + queryDoc, err := getQueriesFromString(string(text), filename) + if err != nil { + return nil, err + } + + mergedQueryDoc.Operations = append(mergedQueryDoc.Operations, queryDoc.Operations...) + mergedQueryDoc.Fragments = append(mergedQueryDoc.Fragments, queryDoc.Fragments...) + + default: + return nil, fmt.Errorf("unknown file type: %v", filename) + } } - // The following is more or less gqlparser.LoadQuery, but we can provide a - // name so we might as well (and we break out the two errors). - document, graphqlError := parser.ParseQuery( - &ast.Source{Name: filename, Input: string(text)}) - if graphqlError != nil { // ParseQuery returns type *graphql.Error, yuck - return nil, fmt.Errorf("invalid query-spec file %v: %v", filename, graphqlError) - } - - graphqlErrors := validator.Validate(schema, document) + graphqlErrors := validator.Validate(schema, mergedQueryDoc) if graphqlErrors != nil { return nil, fmt.Errorf("query-spec does not match schema: %v", graphqlErrors) } + return mergedQueryDoc, nil +} + +func getQueriesFromString(text string, filename string) (*ast.QueryDocument, error) { + document, graphqlError := parser.ParseQuery( + &ast.Source{Name: filename, Input: text}) + if graphqlError != nil { // ParseQuery returns type *graphql.Error, yuck + return nil, fmt.Errorf("invalid query-spec file %v: %v", filename, graphqlError) + } + return document, nil }