big overhaul of error-formatting, to get positions more consistently
fixes #2
This commit is contained in:
+13
-13
@@ -40,6 +40,8 @@ import (
|
||||
// the directive "a" is ignored, "b" and "c" apply to all relevant nodes in the
|
||||
// query, "d" applies to arg2 and arg3, and "e" applies to field1 and field2.
|
||||
type GenqlientDirective struct {
|
||||
pos *ast.Position
|
||||
|
||||
// If set, this argument will be omitted if it's equal to its Go zero
|
||||
// value. For example, given the following query:
|
||||
// # @genqlient(omitempty: true)
|
||||
@@ -68,23 +70,25 @@ func setBool(dst **bool, v *ast.Value) error {
|
||||
ei, err := v.Value(nil) // no vars allowed
|
||||
// TODO: here and below, put positions on these errors
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid boolean value %v: %w", v, err)
|
||||
return errorf(v.Position, "invalid boolean value %v: %v", v, err)
|
||||
}
|
||||
if b, ok := ei.(bool); ok {
|
||||
*dst = &b
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("expected boolean, got non-boolean value %T(%v)", ei, ei)
|
||||
return errorf(v.Position, "expected boolean, got non-boolean value %T(%v)", ei, ei)
|
||||
}
|
||||
|
||||
func fromGraphQL(dir *ast.Directive) (*GenqlientDirective, error) {
|
||||
if dir.Name != "genqlient" {
|
||||
// Actually we just won't get here; we only get here if the line starts
|
||||
// with "# @genqlient", unless there's some sort of bug.
|
||||
return nil, fmt.Errorf("the only valid comment-directive is @genqlient, got %v", dir.Name)
|
||||
return nil, errorf(dir.Position, "the only valid comment-directive is @genqlient, got %v", dir.Name)
|
||||
}
|
||||
|
||||
var retval GenqlientDirective
|
||||
retval.pos = dir.Position
|
||||
|
||||
var err error
|
||||
for _, arg := range dir.Arguments {
|
||||
switch arg.Name {
|
||||
@@ -94,7 +98,7 @@ func fromGraphQL(dir *ast.Directive) (*GenqlientDirective, error) {
|
||||
case "pointer":
|
||||
err = setBool(&retval.Pointer, arg.Value)
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown argument %v for @genqlient", arg.Name)
|
||||
return nil, errorf(arg.Position, "unknown argument %v for @genqlient", arg.Name)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -111,16 +115,16 @@ func (dir *GenqlientDirective) validate(node interface{}) error {
|
||||
return nil
|
||||
case *ast.VariableDefinition:
|
||||
if dir.Omitempty != nil && node.Type.NonNull {
|
||||
return fmt.Errorf("omitempty may only be used on optional arguments")
|
||||
return errorf(dir.pos, "omitempty may only be used on optional arguments")
|
||||
}
|
||||
return nil
|
||||
case *ast.Field:
|
||||
if dir.Omitempty != nil {
|
||||
return fmt.Errorf("omitempty is not appilcable to fields")
|
||||
return errorf(dir.pos, "omitempty is not appilcable to fields")
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("invalid directive location: %T", node)
|
||||
return errorf(dir.pos, "invalid directive location: %T", node)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,13 +178,9 @@ func (g *generator) parsePrecedingComment(
|
||||
func parseDirective(line string, pos *ast.Position) (*ast.Directive, error) {
|
||||
// HACK: parse the "directive" by making a fake query containing it.
|
||||
fakeQuery := fmt.Sprintf("query %v { field }", line)
|
||||
doc, err := parser.ParseQuery(&ast.Source{
|
||||
Name: fmt.Sprintf("@genqlient directive at %v:%v:%v",
|
||||
pos.Src.Name, pos.Line, pos.Column),
|
||||
Input: fakeQuery,
|
||||
})
|
||||
doc, err := parser.ParseQuery(&ast.Source{Input: fakeQuery})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errorf(pos, "invalid genqlient directive: %v", err)
|
||||
}
|
||||
return doc.Operations[0].Directives[0], nil
|
||||
}
|
||||
|
||||
+5
-6
@@ -1,7 +1,6 @@
|
||||
package generate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/token"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
@@ -103,12 +102,12 @@ func (c *Config) ValidateAndFillDefaults(configFilename string) error {
|
||||
if c.Package == "" {
|
||||
abs, err := filepath.Abs(c.Generated)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to guess package-name: %v", err)
|
||||
return errorf(nil, "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)
|
||||
return errorf(nil, "unable to guess package-name: %v is not a valid identifier", base)
|
||||
}
|
||||
|
||||
c.Package = base
|
||||
@@ -122,18 +121,18 @@ func ReadAndValidateConfig(filename string) (*Config, error) {
|
||||
if filename != "" {
|
||||
text, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unreadable config file %v: %v", filename, err)
|
||||
return nil, errorf(nil, "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)
|
||||
return nil, errorf(nil, "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 nil, errorf(nil, "invalid config file %v: %v", filename, err)
|
||||
}
|
||||
|
||||
return &config, nil
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
package generate
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/vektah/gqlparser/v2/ast"
|
||||
"github.com/vektah/gqlparser/v2/gqlerror"
|
||||
)
|
||||
|
||||
type errorPos struct {
|
||||
filename string
|
||||
line, col int
|
||||
}
|
||||
|
||||
func (pos *errorPos) String() string {
|
||||
filename, lineOffset := splitFilename(pos.filename)
|
||||
line := lineOffset + pos.line
|
||||
if line != 0 {
|
||||
return fmt.Sprintf("%v:%v", filename, line)
|
||||
} else {
|
||||
return filename
|
||||
}
|
||||
}
|
||||
|
||||
type genqlientError struct {
|
||||
pos *errorPos
|
||||
msg string
|
||||
wrapped error
|
||||
}
|
||||
|
||||
func splitFilename(filename string) (name string, lineOffset int) {
|
||||
split := strings.Split(filename, ":")
|
||||
if len(split) != 2 {
|
||||
return filename, 0
|
||||
}
|
||||
|
||||
offset, err := strconv.Atoi(split[1])
|
||||
if err != nil {
|
||||
return split[0], 0
|
||||
}
|
||||
return split[0], offset - 1
|
||||
}
|
||||
|
||||
func (err *genqlientError) Error() string {
|
||||
if err.pos != nil {
|
||||
return err.pos.String() + ": " + err.msg
|
||||
} else {
|
||||
return err.msg
|
||||
}
|
||||
}
|
||||
|
||||
func (err *genqlientError) Unwrap() error {
|
||||
return err.wrapped
|
||||
}
|
||||
|
||||
func errorf(pos *ast.Position, msg string, args ...interface{}) error {
|
||||
// TODO: alternately accept a filename only, or maybe even a go-parser pos
|
||||
|
||||
// We do all our own wrapping, because if the wrapped error already has a
|
||||
// pos, we want to extract it out and put it at the front, not in the
|
||||
// middle.
|
||||
|
||||
var wrapped error
|
||||
var wrapIndex int
|
||||
for i, arg := range args {
|
||||
if wrapped == nil {
|
||||
var ok bool
|
||||
wrapped, ok = arg.(error)
|
||||
if ok {
|
||||
wrapIndex = i
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var wrappedGenqlient *genqlientError
|
||||
isGenqlient := errors.As(wrapped, &wrappedGenqlient)
|
||||
var wrappedGraphQL *gqlerror.Error
|
||||
isGraphQL := errors.As(wrapped, &wrappedGraphQL)
|
||||
if !isGraphQL {
|
||||
var wrappedGraphQLList gqlerror.List
|
||||
isGraphQLList := errors.As(wrapped, &wrappedGraphQLList)
|
||||
if isGraphQLList && len(wrappedGraphQLList) > 0 {
|
||||
isGraphQL = true
|
||||
wrappedGraphQL = wrappedGraphQLList[0]
|
||||
}
|
||||
}
|
||||
|
||||
var errPos *errorPos
|
||||
if pos != nil {
|
||||
errPos = &errorPos{
|
||||
filename: pos.Src.Name,
|
||||
line: pos.Line,
|
||||
col: pos.Column,
|
||||
}
|
||||
} else if isGenqlient {
|
||||
errPos = wrappedGenqlient.pos
|
||||
} else if isGraphQL {
|
||||
filename, _ := wrappedGraphQL.Extensions["file"].(string)
|
||||
if filename != "" {
|
||||
var loc gqlerror.Location
|
||||
if len(wrappedGraphQL.Locations) > 0 {
|
||||
loc = wrappedGraphQL.Locations[0]
|
||||
}
|
||||
errPos = &errorPos{
|
||||
filename: filename,
|
||||
line: loc.Line,
|
||||
col: loc.Column,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if wrapped != nil {
|
||||
errText := wrapped.Error()
|
||||
if isGenqlient {
|
||||
errText = wrappedGenqlient.msg
|
||||
} else if isGraphQL {
|
||||
errText = wrappedGraphQL.Message
|
||||
}
|
||||
args[wrapIndex] = errText
|
||||
}
|
||||
|
||||
msg = fmt.Sprintf(msg, args...)
|
||||
|
||||
return &genqlientError{
|
||||
msg: msg,
|
||||
pos: errPos,
|
||||
wrapped: wrapped,
|
||||
}
|
||||
}
|
||||
+10
-8
@@ -3,7 +3,6 @@ package generate
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"sort"
|
||||
"strings"
|
||||
@@ -134,7 +133,7 @@ func (g *generator) getArgument(
|
||||
|
||||
func (g *generator) addOperation(op *ast.OperationDefinition) error {
|
||||
if op.Name == "" {
|
||||
return fmt.Errorf("operations must have operation-names")
|
||||
return errorf(op.Position, "operations must have operation-names")
|
||||
}
|
||||
|
||||
var builder strings.Builder
|
||||
@@ -199,11 +198,14 @@ func Generate(config *Config) (map[string][]byte, error) {
|
||||
// package-name, if it turns out to be more convenient that way. (As-is,
|
||||
// we generate a broken file, with just (unused) imports.)
|
||||
if len(document.Operations) == 0 {
|
||||
return nil, fmt.Errorf("no queries found in %v", config.Operations)
|
||||
// Hard to have a position when there are no operations :(
|
||||
return nil, errorf(nil, "no queries found, looked in: %v",
|
||||
strings.Join(config.Operations, ", "))
|
||||
}
|
||||
|
||||
if len(document.Fragments) > 0 && !allowBrokenFeatures {
|
||||
return nil, fmt.Errorf("genqlient does not yet support fragments")
|
||||
return nil, errorf(document.Fragments[0].Position,
|
||||
"genqlient does not yet support fragments")
|
||||
}
|
||||
|
||||
g := newGenerator(config, schema)
|
||||
@@ -216,18 +218,18 @@ func Generate(config *Config) (map[string][]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
err = g.execute("operation.go.tmpl", &buf, g)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not render template: %v", err)
|
||||
return nil, errorf(nil, "could not render template: %v", err)
|
||||
}
|
||||
|
||||
unformatted := buf.Bytes()
|
||||
formatted, err := format.Source(unformatted)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not gofmt code: %v\n---unformatted code---\n%v",
|
||||
return nil, errorf(nil, "could not gofmt code: %v\n---unformatted code---\n%v",
|
||||
err, string(unformatted))
|
||||
}
|
||||
importsed, err := imports.Process(config.Generated, formatted, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not goimports code: %v\n---unimportsed code---\n%v",
|
||||
return nil, errorf(nil, "could not goimports code: %v\n---unimportsed code---\n%v",
|
||||
err, string(formatted))
|
||||
}
|
||||
|
||||
@@ -243,7 +245,7 @@ func Generate(config *Config) (map[string][]byte, error) {
|
||||
retval[config.ExportOperations], err = json.MarshalIndent(
|
||||
exportedOperations{Operations: g.Operations}, "", " ")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to export queries: %v", err)
|
||||
return nil, errorf(nil, "unable to export queries: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ package generate
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
@@ -20,7 +19,7 @@ func gofmt(filename, src string) (string, error) {
|
||||
src = strings.TrimSpace(src)
|
||||
formatted, err := format.Source([]byte(src))
|
||||
if err != nil {
|
||||
return src, fmt.Errorf("go parse error in %v: %w", filename, err)
|
||||
return src, errorf(nil, "go parse error in %v: %v", filename, err)
|
||||
}
|
||||
return string(formatted), nil
|
||||
}
|
||||
|
||||
+3
-3
@@ -1,7 +1,6 @@
|
||||
package generate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/types"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -45,7 +44,8 @@ func (g *generator) getRef(fullyQualifiedName string, addImport bool) (qualified
|
||||
// confusing, why would you want it. But the empty interface,
|
||||
// specifically, is useful.
|
||||
if fullyQualifiedName != "interface{}" && types.Universe.Lookup(fullyQualifiedName) == nil {
|
||||
return "", fmt.Errorf(
|
||||
// TODO: pass in pos here
|
||||
return "", errorf(nil,
|
||||
`unknown name "%v"; expected a builtin or path/to/package.Name`, fullyQualifiedName)
|
||||
}
|
||||
return fullyQualifiedName, nil
|
||||
@@ -60,7 +60,7 @@ func (g *generator) getRef(fullyQualifiedName string, addImport bool) (qualified
|
||||
var ok bool
|
||||
alias, ok = g.imports[pkgPath]
|
||||
if !ok {
|
||||
return "", fmt.Errorf(`no alias defined for package "%v"`, pkgPath)
|
||||
return "", errorf(nil, `no alias defined for package "%v"`, pkgPath)
|
||||
}
|
||||
}
|
||||
return alias + "." + localName, nil
|
||||
|
||||
+8
-3
@@ -5,6 +5,7 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func readConfigGenerateAndWrite(configFilename string) error {
|
||||
@@ -21,14 +22,14 @@ func readConfigGenerateAndWrite(configFilename string) error {
|
||||
for filename, content := range generated {
|
||||
err = os.MkdirAll(filepath.Dir(filename), 0o755)
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
return errorf(nil,
|
||||
"could not create parent directory for generated file %v: %v",
|
||||
filename, err)
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(filename, content, 0o644)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not write generated file %v: %v",
|
||||
return errorf(nil, "could not write generated file %v: %v",
|
||||
filename, err)
|
||||
}
|
||||
}
|
||||
@@ -50,6 +51,10 @@ func Main() {
|
||||
case 1:
|
||||
err = readConfigGenerateAndWrite("")
|
||||
default:
|
||||
err = fmt.Errorf("usage: %s [config]", os.Args[0])
|
||||
argv0 := os.Args[0]
|
||||
if strings.Contains(argv0, string(filepath.Separator)+"go-build") {
|
||||
argv0 = "go run github.com/Khan/genqlient"
|
||||
}
|
||||
err = errorf(nil, "usage: %s [config]", argv0)
|
||||
}
|
||||
}
|
||||
|
||||
+14
-10
@@ -20,13 +20,13 @@ import (
|
||||
func getSchema(filename string) (*ast.Schema, error) {
|
||||
text, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unreadable schema file %v: %v", filename, err)
|
||||
return nil, errorf(nil, "unreadable schema file %v: %v", filename, err)
|
||||
}
|
||||
|
||||
schema, graphqlError := gqlparser.LoadSchema(
|
||||
&ast.Source{Name: filename, Input: string(text)})
|
||||
if graphqlError != nil {
|
||||
return nil, fmt.Errorf("invalid schema file %v: %v",
|
||||
return nil, errorf(nil, "invalid schema file %v: %v",
|
||||
filename, graphqlError)
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ func getAndValidateQueries(basedir string, filenames []string, schema *ast.Schem
|
||||
// Cf. gqlparser.LoadQuery
|
||||
graphqlErrors := validator.Validate(schema, queryDoc)
|
||||
if graphqlErrors != nil {
|
||||
return nil, fmt.Errorf("query-spec does not match schema: %v", graphqlErrors)
|
||||
return nil, errorf(nil, "query-spec does not match schema: %v", graphqlErrors)
|
||||
}
|
||||
|
||||
return queryDoc, nil
|
||||
@@ -64,7 +64,7 @@ func getQueries(basedir string, filenames []string) (*ast.QueryDocument, error)
|
||||
for _, filename := range filenames {
|
||||
matches, err := filepath.Glob(filename)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't expand file-glob %v: %v", filename, err)
|
||||
return nil, errorf(nil, "can't expand file-glob %v: %v", filename, err)
|
||||
}
|
||||
expandedFilenames = append(expandedFilenames, matches...)
|
||||
}
|
||||
@@ -72,7 +72,7 @@ func getQueries(basedir string, filenames []string) (*ast.QueryDocument, error)
|
||||
for _, filename := range expandedFilenames {
|
||||
text, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unreadable query-spec file %v: %v", filename, err)
|
||||
return nil, errorf(nil, "unreadable query-spec file %v: %v", filename, err)
|
||||
}
|
||||
|
||||
switch filepath.Ext(filename) {
|
||||
@@ -95,7 +95,7 @@ func getQueries(basedir string, filenames []string) (*ast.QueryDocument, error)
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown file type: %v", filename)
|
||||
return nil, errorf(nil, "unknown file type: %v", filename)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ func getQueriesFromString(text string, basedir, filename string) (*ast.QueryDocu
|
||||
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 nil, errorf(nil, "invalid query-spec file %v: %v", filename, graphqlError)
|
||||
}
|
||||
|
||||
return document, nil
|
||||
@@ -123,7 +123,7 @@ func getQueriesFromGo(text string, basedir, filename string) ([]*ast.QueryDocume
|
||||
fset := goToken.NewFileSet()
|
||||
f, err := goParser.ParseFile(fset, filename, text, 0)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid Go file %v: %v", filename, err)
|
||||
return nil, errorf(nil, "invalid Go file %v: %v", filename, err)
|
||||
}
|
||||
|
||||
var retval []*ast.QueryDocument
|
||||
@@ -147,9 +147,13 @@ func getQueriesFromGo(text string, basedir, filename string) ([]*ast.QueryDocume
|
||||
return true
|
||||
}
|
||||
|
||||
filename := fset.Position(basicLit.Pos()).Filename
|
||||
// We put the filename as <real filename>:<line>, which errors.go knows
|
||||
// how to parse back out (since it's what gqlparser will give to us in
|
||||
// our errors).
|
||||
pos := fset.Position(basicLit.Pos())
|
||||
fakeFilename := fmt.Sprintf("%v:%v", pos.Filename, pos.Line)
|
||||
var query *ast.QueryDocument
|
||||
query, err = getQueriesFromString(value, basedir, filename)
|
||||
query, err = getQueriesFromString(value, basedir, fakeFilename)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package generate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
@@ -26,13 +25,13 @@ func (g *generator) execute(tmplRelFilename string, w io.Writer, data interface{
|
||||
var err error
|
||||
tmpl, err = template.New(tmplRelFilename).Funcs(funcMap).ParseFiles(absFilename)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not load template %v: %v", absFilename, err)
|
||||
return errorf(nil, "could not load template %v: %v", absFilename, err)
|
||||
}
|
||||
g.templateCache[tmplRelFilename] = tmpl
|
||||
}
|
||||
err := tmpl.Execute(w, data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not render template: %v", err)
|
||||
return errorf(nil, "could not render template: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
query-spec does not match schema: testdata/errors/InvalidQuery.go:2: Cannot query field "g" on type "Query". Did you mean "f"?
|
||||
testdata/errors/InvalidQuery.go:4: query-spec does not match schema: Cannot query field "g" on type "Query". Did you mean "f"?
|
||||
+1
-1
@@ -1 +1 @@
|
||||
query-spec does not match schema: testdata/errors/InvalidQuery.graphql:1: Cannot query field "g" on type "Query". Did you mean "f"?
|
||||
testdata/errors/InvalidQuery.graphql:1: query-spec does not match schema: Cannot query field "g" on type "Query". Did you mean "f"?
|
||||
+1
-1
@@ -1 +1 @@
|
||||
invalid schema file testdata/errors/InvalidSchema.schema.graphql: testdata/errors/InvalidSchema.schema.graphql:4: Expected :, found }
|
||||
testdata/errors/InvalidSchema.schema.graphql:4: invalid schema file testdata/errors/InvalidSchema.schema.graphql: Expected :, found }
|
||||
+1
-1
@@ -1 +1 @@
|
||||
invalid schema file testdata/errors/InvalidSchema.schema.graphql: testdata/errors/InvalidSchema.schema.graphql:4: Expected :, found }
|
||||
testdata/errors/InvalidSchema.schema.graphql:4: invalid schema file testdata/errors/InvalidSchema.schema.graphql: Expected :, found }
|
||||
+1
-1
@@ -1 +1 @@
|
||||
no queries found in [testdata/errors/NoQuery.go]
|
||||
no queries found, looked in: testdata/errors/NoQuery.go
|
||||
+1
-1
@@ -1 +1 @@
|
||||
no queries found in [testdata/errors/NoQuery.graphql]
|
||||
no queries found, looked in: testdata/errors/NoQuery.graphql
|
||||
+1
-1
@@ -1 +1 @@
|
||||
unknown scalar UnknownScalar: please add it to genqlient.yaml
|
||||
testdata/errors/UnknownScalar.schema.graphql:3: unknown scalar UnknownScalar: please add it to genqlient.yaml
|
||||
+1
-1
@@ -1 +1 @@
|
||||
unknown scalar UnknownScalar: please add it to genqlient.yaml
|
||||
testdata/errors/UnknownScalar.schema.graphql:3: unknown scalar UnknownScalar: please add it to genqlient.yaml
|
||||
+37
-18
@@ -22,11 +22,11 @@ func (g *generator) baseTypeForOperation(operation ast.Operation) (*ast.Definiti
|
||||
return g.schema.Mutation, nil
|
||||
case ast.Subscription:
|
||||
if !allowBrokenFeatures {
|
||||
return nil, fmt.Errorf("genqlient does not yet support subscriptions")
|
||||
return nil, errorf(nil, "genqlient does not yet support subscriptions")
|
||||
}
|
||||
return g.schema.Subscription, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unexpected operation: %v", operation)
|
||||
return nil, errorf(nil, "unexpected operation: %v", operation)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ func (g *generator) getTypeForOperation(operation *ast.OperationDefinition, quer
|
||||
|
||||
if def, ok := g.typeMap[name]; ok {
|
||||
// TODO: check for and handle conflicts a better way
|
||||
return "", fmt.Errorf("%s defined twice:\n%s", name, def)
|
||||
return "", errorf(operation.Position, "%s defined twice:\n%s", name, def)
|
||||
}
|
||||
|
||||
fields, err := selections(g, operation.SelectionSet, queryOptions)
|
||||
@@ -46,10 +46,10 @@ func (g *generator) getTypeForOperation(operation *ast.OperationDefinition, quer
|
||||
|
||||
baseType, err := g.baseTypeForOperation(operation.Operation)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return "", errorf(operation.Position, "%v", err)
|
||||
}
|
||||
|
||||
return g.addTypeForDefinition(operation.Name, name, baseType, fields, queryOptions)
|
||||
return g.addTypeForDefinition(operation.Name, name, baseType, operation.Position, fields, queryOptions)
|
||||
}
|
||||
|
||||
var builtinTypes = map[string]string{
|
||||
@@ -61,7 +61,13 @@ var builtinTypes = map[string]string{
|
||||
"ID": "string",
|
||||
}
|
||||
|
||||
func (g *generator) addTypeForDefinition(namePrefix, nameOverride string, typ *ast.Definition, fields []field, options *GenqlientDirective) (name string, err error) {
|
||||
func (g *generator) addTypeForDefinition(
|
||||
namePrefix, nameOverride string,
|
||||
typ *ast.Definition,
|
||||
pos *ast.Position,
|
||||
fields []field,
|
||||
options *GenqlientDirective,
|
||||
) (name string, err error) {
|
||||
// If this is a builtin type or custom scalar, just refer to it.
|
||||
goName, ok := g.Config.Scalars[typ.Name]
|
||||
if ok {
|
||||
@@ -114,7 +120,7 @@ func (g *generator) addTypeForDefinition(namePrefix, nameOverride string, typ *a
|
||||
// name.
|
||||
builder := &typeBuilder{typeName: name, typeNamePrefix: namePrefix, generator: g}
|
||||
fmt.Fprintf(builder, "type %s ", name)
|
||||
err = builder.writeTypedef(typ, fields, options)
|
||||
err = builder.writeTypedef(typ, pos, fields, options)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -146,6 +152,7 @@ type field interface {
|
||||
Alias() string
|
||||
Options() (*GenqlientDirective, error)
|
||||
Type() *ast.Type
|
||||
Pos() *ast.Position
|
||||
SubFields() ([]field, error)
|
||||
}
|
||||
|
||||
@@ -176,6 +183,10 @@ func (s outputField) Type() *ast.Type {
|
||||
return s.field.Definition.Type
|
||||
}
|
||||
|
||||
func (s outputField) Pos() *ast.Position {
|
||||
return s.field.Position
|
||||
}
|
||||
|
||||
func (s outputField) SubFields() ([]field, error) {
|
||||
return selections(s.generator, s.field.SelectionSet, s.queryOptions)
|
||||
}
|
||||
@@ -186,10 +197,12 @@ func selections(g *generator, selectionSet ast.SelectionSet, options *GenqlientD
|
||||
switch selection := selection.(type) {
|
||||
case *ast.Field:
|
||||
retval[i] = outputField{g, options, selection}
|
||||
case *ast.FragmentSpread, *ast.InlineFragment:
|
||||
return nil, fmt.Errorf("not implemented: %T", selection)
|
||||
case *ast.FragmentSpread:
|
||||
return nil, errorf(selection.Position, "not implemented: %T", selection)
|
||||
case *ast.InlineFragment:
|
||||
return nil, errorf(selection.Position, "not implemented: %T", selection)
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid selection type: %v", selection)
|
||||
return nil, errorf(nil, "invalid selection type: %T", selection)
|
||||
}
|
||||
}
|
||||
return retval, nil
|
||||
@@ -209,7 +222,8 @@ func (s inputField) Options() (*GenqlientDirective, error) {
|
||||
}
|
||||
return s.queryOptions.merge(directive), nil
|
||||
}
|
||||
func (s inputField) Type() *ast.Type { return s.field.Type }
|
||||
func (s inputField) Type() *ast.Type { return s.field.Type }
|
||||
func (s inputField) Pos() *ast.Position { return s.field.Position }
|
||||
|
||||
func (s inputField) SubFields() ([]field, error) {
|
||||
return selectionsForType(s.generator, s.field.Type, s.queryOptions), nil
|
||||
@@ -236,7 +250,7 @@ func (builder *typeBuilder) writeField(field field) error {
|
||||
if typ == nil {
|
||||
// Unclear why gqlparser hasn't already rejected this,
|
||||
// but empirically it might not.
|
||||
return fmt.Errorf("undefined field %v", field.Alias())
|
||||
return errorf(field.Pos(), "undefined field %v", field.Alias())
|
||||
}
|
||||
|
||||
fields, err := field.SubFields()
|
||||
@@ -291,7 +305,7 @@ func (builder *typeBuilder) writeType(namePrefix, nameOverride string, typ *ast.
|
||||
|
||||
def := builder.schema.Types[typ.Name()]
|
||||
// Writes a typedef elsewhere (if not already defined)
|
||||
name, err := builder.addTypeForDefinition(namePrefix, nameOverride, def, fields, options)
|
||||
name, err := builder.addTypeForDefinition(namePrefix, nameOverride, def, typ.Position, fields, options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -300,7 +314,12 @@ func (builder *typeBuilder) writeType(namePrefix, nameOverride string, typ *ast.
|
||||
return nil
|
||||
}
|
||||
|
||||
func (builder *typeBuilder) writeTypedef(typedef *ast.Definition, fields []field, options *GenqlientDirective) error {
|
||||
func (builder *typeBuilder) writeTypedef(
|
||||
typedef *ast.Definition,
|
||||
pos *ast.Position,
|
||||
fields []field,
|
||||
options *GenqlientDirective,
|
||||
) error {
|
||||
switch typedef.Kind {
|
||||
case ast.Object, ast.InputObject:
|
||||
builder.WriteString("struct {\n")
|
||||
@@ -318,7 +337,7 @@ func (builder *typeBuilder) writeTypedef(typedef *ast.Definition, fields []field
|
||||
|
||||
case ast.Interface, ast.Union:
|
||||
if !allowBrokenFeatures {
|
||||
return fmt.Errorf("not implemented: %v", typedef.Kind)
|
||||
return errorf(pos, "not implemented: %v", typedef.Kind)
|
||||
}
|
||||
|
||||
// First, write the interface type.
|
||||
@@ -332,7 +351,7 @@ func (builder *typeBuilder) writeTypedef(typedef *ast.Definition, fields []field
|
||||
// Then, write the implementations.
|
||||
// TODO(benkraft): Put a doc-comment somewhere with the list.
|
||||
for _, impldef := range builder.schema.GetPossibleTypes(typedef) {
|
||||
name, err := builder.addTypeForDefinition(builder.typeNamePrefix, "", impldef, fields, options)
|
||||
name, err := builder.addTypeForDefinition(builder.typeNamePrefix, "", impldef, pos, fields, options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -355,8 +374,8 @@ func (builder *typeBuilder) writeTypedef(typedef *ast.Definition, fields []field
|
||||
builder.WriteString(")\n")
|
||||
return nil
|
||||
case ast.Scalar:
|
||||
return fmt.Errorf("unknown scalar %v: please add it to genqlient.yaml", typedef.Name)
|
||||
return errorf(pos, "unknown scalar %v: please add it to genqlient.yaml", typedef.Name)
|
||||
default:
|
||||
return fmt.Errorf("unexpected kind: %v", typedef.Kind)
|
||||
return errorf(pos, "unexpected kind: %v", typedef.Kind)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user