️ Accept array of string as valid input schema path. (#134)

* Fixes #88. Accept array of string as valid input schema path.
This commit is contained in:
Hasibul Hasan
2021-10-04 13:33:21 -07:00
committed by GitHub
parent 5e3c4d1be8
commit 59b6df6aab
7 changed files with 124 additions and 21 deletions
+76 -3
View File
@@ -6,7 +6,10 @@ import (
"io/ioutil"
"os"
"path/filepath"
"regexp"
"strings"
"github.com/vektah/gqlparser/v2/ast"
"gopkg.in/yaml.v2"
)
@@ -17,7 +20,7 @@ import (
type Config struct {
// The following fields are documented at:
// https://github.com/Khan/genqlient/blob/main/docs/genqlient.yaml
Schema string `yaml:"schema"`
Schema StringList `yaml:"schema"`
Operations []string `yaml:"operations"`
Generated string `yaml:"generated"`
Package string `yaml:"package"`
@@ -55,8 +58,9 @@ type TypeBinding struct {
// 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(baseDir, c.Schema)
for i := range c.Schema {
c.Schema[i] = filepath.Join(baseDir, c.Schema[i])
}
for i := range c.Operations {
c.Operations[i] = filepath.Join(baseDir, c.Operations[i])
}
@@ -122,3 +126,72 @@ 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
}
+3 -3
View File
@@ -76,7 +76,7 @@ func TestGenerate(t *testing.T) {
t.Run(sourceFilename, func(t *testing.T) {
generated, err := Generate(&Config{
Schema: filepath.Join(dataDir, "schema.graphql"),
Schema: []string{filepath.Join(dataDir, "schema.graphql")},
Operations: []string{filepath.Join(dataDir, sourceFilename)},
Package: "test",
Generated: goFilename,
@@ -197,7 +197,7 @@ func TestGenerateWithConfig(t *testing.T) {
baseDir := filepath.Join(dataDir, test.baseDir)
t.Run(test.name, func(t *testing.T) {
err := config.ValidateAndFillDefaults(baseDir)
config.Schema = filepath.Join(dataDir, "schema.graphql")
config.Schema = []string{filepath.Join(dataDir, "schema.graphql")}
config.Operations = []string{filepath.Join(dataDir, sourceFilename)}
if err != nil {
t.Fatal(err)
@@ -256,7 +256,7 @@ func TestGenerateErrors(t *testing.T) {
t.Run(testFilename, func(t *testing.T) {
_, err := Generate(&Config{
Schema: filepath.Join(errorsDir, schemaFilename),
Schema: []string{filepath.Join(errorsDir, schemaFilename)},
Operations: []string{filepath.Join(errorsDir, sourceFilename)},
Package: "test",
Generated: os.DevNull,
+6 -9
View File
@@ -16,19 +16,16 @@ import (
"github.com/vektah/gqlparser/v2/validator"
)
func getSchema(filename string) (*ast.Schema, error) {
text, err := ioutil.ReadFile(filename)
func getSchema(filePatterns StringList) (*ast.Schema, error) {
sources, err := loadSchemaSources(filePatterns)
if err != nil {
return nil, errorf(nil, "unreadable schema file %v: %v", filename, err)
return nil, err
}
schema, graphqlError := gqlparser.LoadSchema(
&ast.Source{Name: filename, Input: string(text)})
schema, graphqlError := gqlparser.LoadSchema(sources...)
if graphqlError != nil {
return nil, errorf(nil, "invalid schema file %v: %v",
filename, graphqlError)
filename, _ := graphqlError.Extensions["file"].(string)
return nil, errorf(nil, "invalid schema file %v: %v", filename, graphqlError)
}
return schema, nil
}
+33
View File
@@ -0,0 +1,33 @@
package generate
// StringList provides yaml unmarshaler to accept both `string` and `[]string` as a valid type.
// Sourced from:
// https://github.com/99designs/gqlgen/blob/1a0b19feff6f02d2af6631c9d847bc243f8ede39/codegen/config/config.go#L302-L329
type StringList []string
func (a *StringList) UnmarshalYAML(unmarshal func(interface{}) error) error {
var single string
err := unmarshal(&single)
if err == nil {
*a = []string{single}
return nil
}
var multi []string
err = unmarshal(&multi)
if err != nil {
return err
}
*a = multi
return nil
}
func (a StringList) Has(file string) bool {
for _, existing := range a {
if existing == file {
return true
}
}
return false
}