Align support for multiple operation-files and multiple schema-files (#137)

Multiple schema-files are now supported as of #134, but the support was
a bit different from how we did multiple operation-files.  Before anyone
starts to depend on the ways the syntaxes differ, let's just make them
the same.  Since it's easy, I also added support for having just a
single operations-file.

I also realized while writing this that the type-change is technically
breaking (if you call from Go), so documented it as such. I think
this is unlikely to affect many people.

Test plan: make check
This commit is contained in:
Ben Kraft
2021-10-05 12:52:48 -07:00
committed by GitHub
parent a52e55632f
commit 22019287da
8 changed files with 49 additions and 103 deletions
+1 -73
View File
@@ -6,10 +6,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
"regexp"
"strings"
"github.com/vektah/gqlparser/v2/ast"
"gopkg.in/yaml.v2"
)
@@ -21,7 +18,7 @@ type Config struct {
// The following fields are documented at:
// https://github.com/Khan/genqlient/blob/main/docs/genqlient.yaml
Schema StringList `yaml:"schema"`
Operations []string `yaml:"operations"`
Operations StringList `yaml:"operations"`
Generated string `yaml:"generated"`
Package string `yaml:"package"`
ExportOperations string `yaml:"export_operations"`
@@ -126,72 +123,3 @@ func initConfig(filename string) error {
_, err = io.Copy(w, r)
return errorf(nil, "unable to write default genqlient.yaml: %v", err)
}
var path2regex = strings.NewReplacer(
`.`, `\.`,
`*`, `.+`,
`\`, `[\\/]`,
`/`, `[\\/]`,
)
// loadSchemaSources parses the schema file path globs. Parses graphql files,
// and returns the parsed ast.Source objects.
// Sourced From:
// https://github.com/99designs/gqlgen/blob/1a0b19feff6f02d2af6631c9d847bc243f8ede39/codegen/config/config.go#L129-L181
func loadSchemaSources(schemas StringList) ([]*ast.Source, error) {
preGlobbing := schemas
schemas = StringList{}
source := make([]*ast.Source, 0)
for _, f := range preGlobbing {
var matches []string
// for ** we want to override default globbing patterns and walk all
// subdirectories to match schema files.
if strings.Contains(f, "**") {
pathParts := strings.SplitN(f, "**", 2)
rest := strings.TrimPrefix(strings.TrimPrefix(pathParts[1], `\`), `/`)
// turn the rest of the glob into a regex, anchored only at the end because ** allows
// for any number of dirs in between and walk will let us match against the full path name
globRe := regexp.MustCompile(path2regex.Replace(rest) + `$`)
if err := filepath.Walk(pathParts[0], func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if globRe.MatchString(strings.TrimPrefix(path, pathParts[0])) {
matches = append(matches, path)
}
return nil
}); err != nil {
return nil, errorf(nil, "failed to walk schema at root %s: %w", pathParts[0], err)
}
} else {
var err error
matches, err = filepath.Glob(f)
if err != nil {
return nil, errorf(nil, "failed to glob schema filename %s: %w", f, err)
}
}
for _, m := range matches {
if schemas.Has(m) {
continue
}
schemas = append(schemas, m)
}
}
for _, filename := range schemas {
filename = filepath.ToSlash(filename)
var err error
var schemaRaw []byte
schemaRaw, err = ioutil.ReadFile(filename)
if err != nil {
return nil, errorf(nil, "unable to open schema: %w", err)
}
source = append(source, &ast.Source{Name: filename, Input: string(schemaRaw)})
}
return source, nil
}
+38 -14
View File
@@ -16,20 +16,30 @@ import (
"github.com/vektah/gqlparser/v2/validator"
)
func getSchema(filePatterns StringList) (*ast.Schema, error) {
sources, err := loadSchemaSources(filePatterns)
func getSchema(globs StringList) (*ast.Schema, error) {
filenames, err := expandFilenames(globs)
if err != nil {
return nil, err
}
sources := make([]*ast.Source, len(filenames))
for i, filename := range filenames {
text, err := ioutil.ReadFile(filename)
if err != nil {
return nil, errorf(nil, "unreadable schema file %v: %v", filename, err)
}
sources[i] = &ast.Source{Name: filename, Input: string(text)}
}
schema, graphqlError := gqlparser.LoadSchema(sources...)
if graphqlError != nil {
filename, _ := graphqlError.Extensions["file"].(string)
return nil, errorf(nil, "invalid schema file %v: %v", filename, graphqlError)
return nil, errorf(nil, "invalid schema: %v", graphqlError)
}
return schema, nil
}
func getAndValidateQueries(basedir string, filenames []string, schema *ast.Schema) (*ast.QueryDocument, error) {
func getAndValidateQueries(basedir string, filenames StringList, schema *ast.Schema) (*ast.QueryDocument, error) {
queryDoc, err := getQueries(basedir, filenames)
if err != nil {
return nil, err
@@ -44,7 +54,25 @@ func getAndValidateQueries(basedir string, filenames []string, schema *ast.Schem
return queryDoc, nil
}
func getQueries(basedir string, filenames []string) (*ast.QueryDocument, error) {
func expandFilenames(globs []string) ([]string, error) {
uniqFilenames := make(map[string]bool, len(globs))
for _, glob := range globs {
matches, err := filepath.Glob(glob)
if err != nil {
return nil, errorf(nil, "can't expand file-glob %v: %v", glob, err)
}
for _, match := range matches {
uniqFilenames[match] = true
}
}
filenames := make([]string, 0, len(uniqFilenames))
for filename := range uniqFilenames {
filenames = append(filenames, filename)
}
return filenames, nil
}
func getQueries(basedir string, globs StringList) (*ast.QueryDocument, error) {
// We merge all the queries into a single query-document, since operations
// in one might reference fragments in another.
//
@@ -57,16 +85,12 @@ func getQueries(basedir string, filenames []string) (*ast.QueryDocument, error)
mergedQueryDoc.Fragments = append(mergedQueryDoc.Fragments, queryDoc.Fragments...)
}
expandedFilenames := make([]string, 0, len(filenames))
for _, filename := range filenames {
matches, err := filepath.Glob(filename)
if err != nil {
return nil, errorf(nil, "can't expand file-glob %v: %v", filename, err)
}
expandedFilenames = append(expandedFilenames, matches...)
filenames, err := expandFilenames(globs)
if err != nil {
return nil, err
}
for _, filename := range expandedFilenames {
for _, filename := range filenames {
text, err := ioutil.ReadFile(filename)
if err != nil {
return nil, errorf(nil, "unreadable query-spec file %v: %v", filename, err)
-9
View File
@@ -22,12 +22,3 @@ func (a *StringList) UnmarshalYAML(unmarshal func(interface{}) error) error {
*a = multi
return nil
}
func (a StringList) Has(file string) bool {
for _, existing := range a {
if existing == file {
return true
}
}
return false
}
@@ -1 +1 @@
testdata/errors/InvalidSchema.schema.graphql:4: invalid schema file testdata/errors/InvalidSchema.schema.graphql: Expected :, found }
testdata/errors/InvalidSchema.schema.graphql:4: invalid schema: Expected :, found }
@@ -1 +1 @@
testdata/errors/InvalidSchema.schema.graphql:4: invalid schema file testdata/errors/InvalidSchema.schema.graphql: Expected :, found }
testdata/errors/InvalidSchema.schema.graphql:4: invalid schema: Expected :, found }