vendor: github.com/vektah/gqlparser/v2 v2.5.32
- Add formatter.WithNonIntrospectionBuiltin - Add a nil check in ArgumentMap - fix(validator): allow nullable variables for nonnull args with default - lint and format full diff: https://github.com/vektah/gqlparser/compare/v2.5.30...v2.5.32 Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
@@ -207,7 +207,7 @@ require (
|
||||
github.com/transparency-dev/formats v0.1.1 // indirect
|
||||
github.com/transparency-dev/merkle v0.0.2 // indirect
|
||||
github.com/valyala/fastjson v1.6.7 // indirect
|
||||
github.com/vektah/gqlparser/v2 v2.5.30 // indirect
|
||||
github.com/vektah/gqlparser/v2 v2.5.32 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||
|
||||
@@ -577,8 +577,8 @@ github.com/valyala/fastjson v1.6.7 h1:ZE4tRy0CIkh+qDc5McjatheGX2czdn8slQjomexVpB
|
||||
github.com/valyala/fastjson v1.6.7/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
|
||||
github.com/vbatts/tar-split v0.12.3 h1:Cd46rkGXI3Td4yrVNwU8ripbxFaQbmesqhjBUUYAJSw=
|
||||
github.com/vbatts/tar-split v0.12.3/go.mod h1:sQOc6OlqGCr7HkGx/IDBeKiTIvqhmj8KffNhEXG4Nq0=
|
||||
github.com/vektah/gqlparser/v2 v2.5.30 h1:EqLwGAFLIzt1wpx1IPpY67DwUujF1OfzgEyDsLrN6kE=
|
||||
github.com/vektah/gqlparser/v2 v2.5.30/go.mod h1:D1/VCZtV3LPnQrcPBeR/q5jkSQIPti0uYCP/RI0gIeo=
|
||||
github.com/vektah/gqlparser/v2 v2.5.32 h1:k9QPJd4sEDTL+qB4ncPLflqTJ3MmjB9SrVzJrawpFSc=
|
||||
github.com/vektah/gqlparser/v2 v2.5.32/go.mod h1:c1I28gSOVNzlfc4WuDlqU7voQnsqI6OG2amkBAFmgts=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
|
||||
|
||||
+7
-3
@@ -1,11 +1,15 @@
|
||||
package ast
|
||||
|
||||
func arg2map(defs ArgumentDefinitionList, args ArgumentList, vars map[string]interface{}) map[string]interface{} {
|
||||
result := map[string]interface{}{}
|
||||
func arg2map(
|
||||
defs ArgumentDefinitionList,
|
||||
args ArgumentList,
|
||||
vars map[string]any,
|
||||
) map[string]any {
|
||||
result := map[string]any{}
|
||||
var err error
|
||||
|
||||
for _, argDef := range defs {
|
||||
var val interface{}
|
||||
var val any
|
||||
var hasValue bool
|
||||
|
||||
if argValue := args.ForName(argDef.Name); argValue != nil {
|
||||
|
||||
+3
-6
@@ -1,5 +1,7 @@
|
||||
package ast
|
||||
|
||||
import "slices"
|
||||
|
||||
type DefinitionKind string
|
||||
|
||||
const (
|
||||
@@ -54,12 +56,7 @@ func (d *Definition) IsInputType() bool {
|
||||
}
|
||||
|
||||
func (d *Definition) OneOf(types ...string) bool {
|
||||
for _, t := range types {
|
||||
if d.Name == t {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
return slices.Contains(types, d.Name)
|
||||
}
|
||||
|
||||
type FieldDefinition struct {
|
||||
|
||||
+6
-3
@@ -3,7 +3,7 @@ package ast
|
||||
type DirectiveLocation string
|
||||
|
||||
const (
|
||||
// Executable
|
||||
// Executable.
|
||||
LocationQuery DirectiveLocation = `QUERY`
|
||||
LocationMutation DirectiveLocation = `MUTATION`
|
||||
LocationSubscription DirectiveLocation = `SUBSCRIPTION`
|
||||
@@ -12,7 +12,7 @@ const (
|
||||
LocationFragmentSpread DirectiveLocation = `FRAGMENT_SPREAD`
|
||||
LocationInlineFragment DirectiveLocation = `INLINE_FRAGMENT`
|
||||
|
||||
// Type System
|
||||
// Type System.
|
||||
LocationSchema DirectiveLocation = `SCHEMA`
|
||||
LocationScalar DirectiveLocation = `SCALAR`
|
||||
LocationObject DirectiveLocation = `OBJECT`
|
||||
@@ -38,6 +38,9 @@ type Directive struct {
|
||||
Location DirectiveLocation
|
||||
}
|
||||
|
||||
func (d *Directive) ArgumentMap(vars map[string]interface{}) map[string]interface{} {
|
||||
func (d *Directive) ArgumentMap(vars map[string]any) map[string]any {
|
||||
if d.Definition == nil {
|
||||
return nil
|
||||
}
|
||||
return arg2map(d.Definition.Arguments, d.Arguments, vars)
|
||||
}
|
||||
|
||||
+4
-3
@@ -42,7 +42,7 @@ type Schema struct {
|
||||
Comment *CommentGroup
|
||||
}
|
||||
|
||||
// AddTypes is the helper to add types definition to the schema
|
||||
// AddTypes is the helper to add types definition to the schema.
|
||||
func (s *Schema) AddTypes(defs ...*Definition) {
|
||||
if s.Types == nil {
|
||||
s.Types = make(map[string]*Definition)
|
||||
@@ -56,7 +56,7 @@ func (s *Schema) AddPossibleType(name string, def *Definition) {
|
||||
s.PossibleTypes[name] = append(s.PossibleTypes[name], def)
|
||||
}
|
||||
|
||||
// GetPossibleTypes will enumerate all the definitions for a given interface or union
|
||||
// GetPossibleTypes will enumerate all the definitions for a given interface or union.
|
||||
func (s *Schema) GetPossibleTypes(def *Definition) []*Definition {
|
||||
return s.PossibleTypes[def.Name]
|
||||
}
|
||||
@@ -65,7 +65,8 @@ func (s *Schema) AddImplements(name string, iface *Definition) {
|
||||
s.Implements[name] = append(s.Implements[name], iface)
|
||||
}
|
||||
|
||||
// GetImplements returns all the interface and union definitions that the given definition satisfies
|
||||
// GetImplements returns all the interface and union definitions that the given definition
|
||||
// satisfies.
|
||||
func (s *Schema) GetImplements(def *Definition) []*Definition {
|
||||
return s.Implements[def.Name]
|
||||
}
|
||||
|
||||
+8
-6
@@ -8,8 +8,8 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Dump turns ast into a stable string format for assertions in tests
|
||||
func Dump(i interface{}) string {
|
||||
// Dump turns ast into a stable string format for assertions in tests.
|
||||
func Dump(i any) string {
|
||||
v := reflect.ValueOf(i)
|
||||
|
||||
d := dumper{Buffer: &bytes.Buffer{}}
|
||||
@@ -126,7 +126,6 @@ func isZero(v reflect.Value) bool {
|
||||
return v.IsNil()
|
||||
case reflect.Func, reflect.Map:
|
||||
return v.IsNil()
|
||||
|
||||
case reflect.Array, reflect.Slice:
|
||||
if v.IsNil() {
|
||||
return true
|
||||
@@ -144,10 +143,13 @@ func isZero(v reflect.Value) bool {
|
||||
return z
|
||||
case reflect.String:
|
||||
return v.String() == ""
|
||||
case reflect.Bool:
|
||||
// Never consider Bool field as zero value.
|
||||
// Always include them in AST dump.
|
||||
return false
|
||||
default:
|
||||
return reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface())
|
||||
}
|
||||
|
||||
// Compare other types directly:
|
||||
return reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()))
|
||||
}
|
||||
|
||||
func (d *dumper) dumpPtr(v reflect.Value) {
|
||||
|
||||
+2
-2
@@ -27,7 +27,7 @@ func (path Path) String() string {
|
||||
for i, v := range path {
|
||||
switch v := v.(type) {
|
||||
case PathIndex:
|
||||
str.WriteString(fmt.Sprintf("[%d]", v))
|
||||
fmt.Fprintf(&str, "[%d]", v)
|
||||
case PathName:
|
||||
if i != 0 {
|
||||
str.WriteByte('.')
|
||||
@@ -41,7 +41,7 @@ func (path Path) String() string {
|
||||
}
|
||||
|
||||
func (path *Path) UnmarshalJSON(b []byte) error {
|
||||
var vs []interface{}
|
||||
var vs []any
|
||||
err := json.Unmarshal(b, &vs)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
+4
-1
@@ -36,6 +36,9 @@ type Argument struct {
|
||||
Comment *CommentGroup
|
||||
}
|
||||
|
||||
func (f *Field) ArgumentMap(vars map[string]interface{}) map[string]interface{} {
|
||||
func (f *Field) ArgumentMap(vars map[string]any) map[string]any {
|
||||
if f.Definition == nil {
|
||||
return nil
|
||||
}
|
||||
return arg2map(f.Definition.Arguments, f.Arguments, vars)
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
package ast
|
||||
|
||||
// Source covers a single *.graphql file
|
||||
// Source covers a single *.graphql file.
|
||||
type Source struct {
|
||||
// Name is the filename of the source
|
||||
Name string
|
||||
|
||||
+7
-6
@@ -29,9 +29,10 @@ type Value struct {
|
||||
Comment *CommentGroup
|
||||
|
||||
// Require validation
|
||||
Definition *Definition
|
||||
VariableDefinition *VariableDefinition
|
||||
ExpectedType *Type
|
||||
Definition *Definition
|
||||
VariableDefinition *VariableDefinition
|
||||
ExpectedType *Type
|
||||
ExpectedTypeHasDefault bool
|
||||
}
|
||||
|
||||
type ChildValue struct {
|
||||
@@ -41,7 +42,7 @@ type ChildValue struct {
|
||||
Comment *CommentGroup
|
||||
}
|
||||
|
||||
func (v *Value) Value(vars map[string]interface{}) (interface{}, error) {
|
||||
func (v *Value) Value(vars map[string]any) (any, error) {
|
||||
if v == nil {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -65,7 +66,7 @@ func (v *Value) Value(vars map[string]interface{}) (interface{}, error) {
|
||||
case NullValue:
|
||||
return nil, nil
|
||||
case ListValue:
|
||||
var val []interface{}
|
||||
var val []any
|
||||
for _, elem := range v.Children {
|
||||
elemVal, err := elem.Value.Value(vars)
|
||||
if err != nil {
|
||||
@@ -75,7 +76,7 @@ func (v *Value) Value(vars map[string]interface{}) (interface{}, error) {
|
||||
}
|
||||
return val, nil
|
||||
case ObjectValue:
|
||||
val := map[string]interface{}{}
|
||||
val := map[string]any{}
|
||||
for _, elem := range v.Children {
|
||||
elemVal, err := elem.Value.Value(vars)
|
||||
if err != nil {
|
||||
|
||||
+16
-15
@@ -11,12 +11,12 @@ import (
|
||||
|
||||
// Error is the standard graphql error type described in https://spec.graphql.org/draft/#sec-Errors
|
||||
type Error struct {
|
||||
Err error `json:"-"`
|
||||
Message string `json:"message"`
|
||||
Path ast.Path `json:"path,omitempty"`
|
||||
Locations []Location `json:"locations,omitempty"`
|
||||
Extensions map[string]interface{} `json:"extensions,omitempty"`
|
||||
Rule string `json:"-"`
|
||||
Err error `json:"-"`
|
||||
Message string `json:"message"`
|
||||
Path ast.Path `json:"path,omitempty"`
|
||||
Locations []Location `json:"locations,omitempty"`
|
||||
Extensions map[string]any `json:"extensions,omitempty"`
|
||||
Rule string `json:"-"`
|
||||
}
|
||||
|
||||
func (err *Error) SetFile(file string) {
|
||||
@@ -24,7 +24,7 @@ func (err *Error) SetFile(file string) {
|
||||
return
|
||||
}
|
||||
if err.Extensions == nil {
|
||||
err.Extensions = map[string]interface{}{}
|
||||
err.Extensions = map[string]any{}
|
||||
}
|
||||
|
||||
err.Extensions["file"] = file
|
||||
@@ -99,7 +99,7 @@ func (errs List) Is(target error) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (errs List) As(target interface{}) bool {
|
||||
func (errs List) As(target any) bool {
|
||||
for _, err := range errs {
|
||||
if errors.As(err, target) {
|
||||
return true
|
||||
@@ -141,7 +141,8 @@ func WrapIfUnwrapped(err error) *Error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
if gqlErr, ok := err.(*Error); ok {
|
||||
gqlErr := &Error{}
|
||||
if errors.As(err, &gqlErr) {
|
||||
return gqlErr
|
||||
}
|
||||
return &Error{
|
||||
@@ -150,20 +151,20 @@ func WrapIfUnwrapped(err error) *Error {
|
||||
}
|
||||
}
|
||||
|
||||
func Errorf(message string, args ...interface{}) *Error {
|
||||
func Errorf(message string, args ...any) *Error {
|
||||
return &Error{
|
||||
Message: fmt.Sprintf(message, args...),
|
||||
}
|
||||
}
|
||||
|
||||
func ErrorPathf(path ast.Path, message string, args ...interface{}) *Error {
|
||||
func ErrorPathf(path ast.Path, message string, args ...any) *Error {
|
||||
return &Error{
|
||||
Message: fmt.Sprintf(message, args...),
|
||||
Path: path,
|
||||
}
|
||||
}
|
||||
|
||||
func ErrorPosf(pos *ast.Position, message string, args ...interface{}) *Error {
|
||||
func ErrorPosf(pos *ast.Position, message string, args ...any) *Error {
|
||||
if pos == nil {
|
||||
return ErrorLocf(
|
||||
"",
|
||||
@@ -182,10 +183,10 @@ func ErrorPosf(pos *ast.Position, message string, args ...interface{}) *Error {
|
||||
)
|
||||
}
|
||||
|
||||
func ErrorLocf(file string, line int, col int, message string, args ...interface{}) *Error {
|
||||
var extensions map[string]interface{}
|
||||
func ErrorLocf(file string, line, col int, message string, args ...any) *Error {
|
||||
var extensions map[string]any
|
||||
if file != "" {
|
||||
extensions = map[string]interface{}{"file": file}
|
||||
extensions = map[string]any{"file": file}
|
||||
}
|
||||
return &Error{
|
||||
Message: fmt.Sprintf(message, args...),
|
||||
|
||||
+79
-22
@@ -2,13 +2,14 @@ package lexer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"slices"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/vektah/gqlparser/v2/ast"
|
||||
"github.com/vektah/gqlparser/v2/gqlerror"
|
||||
)
|
||||
|
||||
// Lexer turns graphql request and schema strings into tokens
|
||||
// Lexer turns graphql request and schema strings into tokens.
|
||||
type Lexer struct {
|
||||
*ast.Source
|
||||
// An offset into the string in bytes
|
||||
@@ -32,7 +33,7 @@ func New(src *ast.Source) Lexer {
|
||||
}
|
||||
}
|
||||
|
||||
// take one rune from input and advance end
|
||||
// take one rune from input and advance end.
|
||||
func (s *Lexer) peek() (rune, int) {
|
||||
return utf8.DecodeRuneInString(s.Input[s.end:])
|
||||
}
|
||||
@@ -55,7 +56,7 @@ func (s *Lexer) makeValueToken(kind Type, value string) (Token, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Lexer) makeError(format string, args ...interface{}) (Token, *gqlerror.Error) {
|
||||
func (s *Lexer) makeError(format string, args ...any) (Token, *gqlerror.Error) {
|
||||
column := s.endRunes - s.lineStartRunes + 1
|
||||
return Token{
|
||||
Kind: Invalid,
|
||||
@@ -122,7 +123,59 @@ func (s *Lexer) ReadToken() (Token, error) {
|
||||
case '#':
|
||||
return s.readComment()
|
||||
|
||||
case '_', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z':
|
||||
case '_',
|
||||
'a',
|
||||
'b',
|
||||
'c',
|
||||
'd',
|
||||
'e',
|
||||
'f',
|
||||
'g',
|
||||
'h',
|
||||
'i',
|
||||
'j',
|
||||
'k',
|
||||
'l',
|
||||
'm',
|
||||
'n',
|
||||
'o',
|
||||
'p',
|
||||
'q',
|
||||
'r',
|
||||
's',
|
||||
't',
|
||||
'u',
|
||||
'v',
|
||||
'w',
|
||||
'x',
|
||||
'y',
|
||||
'z',
|
||||
'A',
|
||||
'B',
|
||||
'C',
|
||||
'D',
|
||||
'E',
|
||||
'F',
|
||||
'G',
|
||||
'H',
|
||||
'I',
|
||||
'J',
|
||||
'K',
|
||||
'L',
|
||||
'M',
|
||||
'N',
|
||||
'O',
|
||||
'P',
|
||||
'Q',
|
||||
'R',
|
||||
'S',
|
||||
'T',
|
||||
'U',
|
||||
'V',
|
||||
'W',
|
||||
'X',
|
||||
'Y',
|
||||
'Z':
|
||||
return s.readName()
|
||||
|
||||
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
@@ -144,14 +197,16 @@ func (s *Lexer) ReadToken() (Token, error) {
|
||||
}
|
||||
|
||||
if r == '\'' {
|
||||
return s.makeError(`Unexpected single quote character ('), did you mean to use a double quote (")?`)
|
||||
return s.makeError(
|
||||
`Unexpected single quote character ('), did you mean to use a double quote (")?`,
|
||||
)
|
||||
}
|
||||
|
||||
return s.makeError(`Cannot parse the unexpected character "%s".`, string(r))
|
||||
}
|
||||
|
||||
// ws reads from body starting at startPosition until it finds a non-whitespace
|
||||
// or commented character, and updates the token end to include all whitespace
|
||||
// or commented character, and updates the token end to include all whitespace.
|
||||
func (s *Lexer) ws() {
|
||||
for s.end < len(s.Input) {
|
||||
switch s.Input[s.end] {
|
||||
@@ -189,7 +244,7 @@ func (s *Lexer) ws() {
|
||||
|
||||
// readComment from the input
|
||||
//
|
||||
// #[\u0009\u0020-\uFFFF]*
|
||||
// #[\u0009\u0020-\uFFFF]*.
|
||||
func (s *Lexer) readComment() (Token, error) {
|
||||
for s.end < len(s.Input) {
|
||||
r, w := s.peek()
|
||||
@@ -256,23 +311,21 @@ func (s *Lexer) readNumber() (Token, error) {
|
||||
return s.makeToken(Int)
|
||||
}
|
||||
|
||||
// acceptByte if it matches any of given bytes, returning true if it found anything
|
||||
// acceptByte if it matches any of given bytes, returning true if it found anything.
|
||||
func (s *Lexer) acceptByte(bytes ...uint8) bool {
|
||||
if s.end >= len(s.Input) {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, accepted := range bytes {
|
||||
if s.Input[s.end] == accepted {
|
||||
s.end++
|
||||
s.endRunes++
|
||||
return true
|
||||
}
|
||||
if slices.Contains(bytes, s.Input[s.end]) {
|
||||
s.end++
|
||||
s.endRunes++
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// acceptDigits from the input, returning the number of digits it found
|
||||
// acceptDigits from the input, returning the number of digits it found.
|
||||
func (s *Lexer) acceptDigits() int {
|
||||
consumed := 0
|
||||
for s.end < len(s.Input) && s.Input[s.end] >= '0' && s.Input[s.end] <= '9' {
|
||||
@@ -285,7 +338,7 @@ func (s *Lexer) acceptDigits() int {
|
||||
}
|
||||
|
||||
// describeNext peeks at the input and returns a human readable string. This should will alloc
|
||||
// and should only be used in errors
|
||||
// and should only be used in errors.
|
||||
func (s *Lexer) describeNext() string {
|
||||
if s.end < len(s.Input) {
|
||||
return `"` + string(s.Input[s.end]) + `"`
|
||||
@@ -295,7 +348,7 @@ func (s *Lexer) describeNext() string {
|
||||
|
||||
// readString from the input
|
||||
//
|
||||
// "([^"\\\u000A\u000D]|(\\(u[0-9a-fA-F]{4}|["\\/bfnrt])))*"
|
||||
// "([^"\\\u000A\u000D]|(\\(u[0-9a-fA-F]{4}|["\\/bfnrt])))*".
|
||||
func (s *Lexer) readString() (Token, error) {
|
||||
inputLen := len(s.Input)
|
||||
|
||||
@@ -332,7 +385,8 @@ func (s *Lexer) readString() (Token, error) {
|
||||
|
||||
case '"':
|
||||
t, err := s.makeToken(String)
|
||||
// the token should not include the quotes in its value, but should cover them in its position
|
||||
// the token should not include the quotes in its value, but should cover them in its
|
||||
// position
|
||||
t.Pos.Start--
|
||||
t.Pos.End++
|
||||
|
||||
@@ -370,7 +424,10 @@ func (s *Lexer) readString() (Token, error) {
|
||||
if !ok {
|
||||
s.end++
|
||||
s.endRunes++
|
||||
return s.makeError("Invalid character escape sequence: \\%s.", s.Input[s.end:s.end+5])
|
||||
return s.makeError(
|
||||
"Invalid character escape sequence: \\%s.",
|
||||
s.Input[s.end:s.end+5],
|
||||
)
|
||||
}
|
||||
buf.WriteRune(r)
|
||||
s.end += 6
|
||||
@@ -405,7 +462,7 @@ func (s *Lexer) readString() (Token, error) {
|
||||
|
||||
// readBlockString from the input
|
||||
//
|
||||
// """("?"?(\\"""|\\(?!=""")|[^"\\]))*"""
|
||||
// """("?"?(\\"""|\\(?!=""")|[^"\\]))*""".
|
||||
func (s *Lexer) readBlockString() (Token, error) {
|
||||
inputLen := len(s.Input)
|
||||
|
||||
@@ -433,7 +490,7 @@ func (s *Lexer) readBlockString() (Token, error) {
|
||||
// If we have at least 3 quotes, use the last 3 as the closing quote
|
||||
if quoteCount >= 3 {
|
||||
// Add any extra quotes to the buffer (except the last 3)
|
||||
for j := 0; j < quoteCount-3; j++ {
|
||||
for range quoteCount - 3 {
|
||||
buf.WriteByte('"')
|
||||
}
|
||||
|
||||
@@ -508,7 +565,7 @@ func unhex(b string) (v rune, ok bool) {
|
||||
|
||||
// readName from the input
|
||||
//
|
||||
// [_A-Za-z][_0-9A-Za-z]*
|
||||
// [_A-Za-z][_0-9A-Za-z]*.
|
||||
func (s *Lexer) readName() (Token, error) {
|
||||
for s.end < len(s.Input) {
|
||||
r, w := s.peek()
|
||||
|
||||
+3
-3
@@ -91,7 +91,7 @@ func (p *parser) peek() lexer.Token {
|
||||
return p.peekToken
|
||||
}
|
||||
|
||||
func (p *parser) error(tok lexer.Token, format string, args ...interface{}) {
|
||||
func (p *parser) error(tok lexer.Token, format string, args ...any) {
|
||||
if p.err != nil {
|
||||
return
|
||||
}
|
||||
@@ -165,7 +165,7 @@ func (p *parser) unexpectedToken(tok lexer.Token) {
|
||||
p.error(tok, "Unexpected %s", tok.String())
|
||||
}
|
||||
|
||||
func (p *parser) many(start lexer.Type, end lexer.Type, cb func()) {
|
||||
func (p *parser) many(start, end lexer.Type, cb func()) {
|
||||
hasDef := p.skip(start)
|
||||
if !hasDef {
|
||||
return
|
||||
@@ -177,7 +177,7 @@ func (p *parser) many(start lexer.Type, end lexer.Type, cb func()) {
|
||||
p.next()
|
||||
}
|
||||
|
||||
func (p *parser) some(start lexer.Type, end lexer.Type, cb func()) *ast.CommentGroup {
|
||||
func (p *parser) some(start, end lexer.Type, cb func()) *ast.CommentGroup {
|
||||
hasDef := p.skip(start)
|
||||
if !hasDef {
|
||||
return nil
|
||||
|
||||
+7
-3
@@ -1,9 +1,8 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"github.com/vektah/gqlparser/v2/lexer"
|
||||
|
||||
. "github.com/vektah/gqlparser/v2/ast" //nolint:staticcheck // bad, yeah
|
||||
"github.com/vektah/gqlparser/v2/lexer"
|
||||
)
|
||||
|
||||
func ParseQuery(source *Source) (*QueryDocument, error) {
|
||||
@@ -259,7 +258,12 @@ func (p *parser) parseValueLiteral(isConst bool) *Value {
|
||||
p.unexpectedError()
|
||||
return nil
|
||||
}
|
||||
return &Value{Position: &token.Pos, Comment: p.comment, Raw: p.parseVariable(), Kind: Variable}
|
||||
return &Value{
|
||||
Position: &token.Pos,
|
||||
Comment: p.comment,
|
||||
Raw: p.parseVariable(),
|
||||
Kind: Variable,
|
||||
}
|
||||
case lexer.Int:
|
||||
kind = IntValue
|
||||
case lexer.Float:
|
||||
|
||||
+8
-11
@@ -8,11 +8,12 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/agnivade/levenshtein"
|
||||
|
||||
"github.com/vektah/gqlparser/v2/ast"
|
||||
"github.com/vektah/gqlparser/v2/gqlerror"
|
||||
)
|
||||
|
||||
func Message(msg string, args ...interface{}) ErrorOption {
|
||||
func Message(msg string, args ...any) ErrorOption {
|
||||
return func(err *gqlerror.Error) {
|
||||
err.Message += fmt.Sprintf(msg, args...)
|
||||
}
|
||||
@@ -33,7 +34,7 @@ func At(position *ast.Position) ErrorOption {
|
||||
}
|
||||
}
|
||||
|
||||
func SuggestListQuoted(prefix string, typed string, suggestions []string) ErrorOption {
|
||||
func SuggestListQuoted(prefix, typed string, suggestions []string) ErrorOption {
|
||||
suggested := SuggestionList(typed, suggestions)
|
||||
return func(err *gqlerror.Error) {
|
||||
if len(suggested) > 0 {
|
||||
@@ -42,7 +43,7 @@ func SuggestListQuoted(prefix string, typed string, suggestions []string) ErrorO
|
||||
}
|
||||
}
|
||||
|
||||
func SuggestListUnquoted(prefix string, typed string, suggestions []string) ErrorOption {
|
||||
func SuggestListUnquoted(prefix, typed string, suggestions []string) ErrorOption {
|
||||
suggested := SuggestionList(typed, suggestions)
|
||||
return func(err *gqlerror.Error) {
|
||||
if len(suggested) > 0 {
|
||||
@@ -51,7 +52,7 @@ func SuggestListUnquoted(prefix string, typed string, suggestions []string) Erro
|
||||
}
|
||||
}
|
||||
|
||||
func Suggestf(suggestion string, args ...interface{}) ErrorOption {
|
||||
func Suggestf(suggestion string, args ...any) ErrorOption {
|
||||
return func(err *gqlerror.Error) {
|
||||
err.Message += " Did you mean " + fmt.Sprintf(suggestion, args...) + "?"
|
||||
}
|
||||
@@ -117,12 +118,8 @@ func SuggestionList(input string, options []string) []string {
|
||||
func calcThreshold(a string) (threshold int) {
|
||||
// the logic is copied from here
|
||||
// https://github.com/graphql/graphql-js/blob/47bd8c8897c72d3efc17ecb1599a95cee6bac5e8/src/jsutils/suggestionList.ts#L14
|
||||
threshold = int(math.Floor(float64(len(a))*0.4) + 1)
|
||||
|
||||
if threshold < 1 {
|
||||
threshold = 1
|
||||
}
|
||||
return
|
||||
threshold = max(int(math.Floor(float64(len(a))*0.4)+1), 1)
|
||||
return threshold
|
||||
}
|
||||
|
||||
// Computes the lexical distance between strings A and B.
|
||||
@@ -136,7 +133,7 @@ func calcThreshold(a string) (threshold int) {
|
||||
// as a single edit which helps identify mis-cased values with an edit distance
|
||||
// of 1.
|
||||
//
|
||||
// This distance can be useful for detecting typos in input or sorting
|
||||
// This distance can be useful for detecting typos in input or sorting.
|
||||
func lexicalDistance(a, b string) int {
|
||||
if a == b {
|
||||
return 0
|
||||
|
||||
+9
-1
@@ -142,7 +142,11 @@ func (w *Walker) walkFragment(it *ast.FragmentDefinition) {
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Walker) walkDirectives(parentDef *ast.Definition, directives []*ast.Directive, location ast.DirectiveLocation) {
|
||||
func (w *Walker) walkDirectives(
|
||||
parentDef *ast.Definition,
|
||||
directives []*ast.Directive,
|
||||
location ast.DirectiveLocation,
|
||||
) {
|
||||
for _, dir := range directives {
|
||||
def := w.Schema.Directives[dir.Name]
|
||||
dir.Definition = def
|
||||
@@ -182,6 +186,8 @@ func (w *Walker) walkValue(value *ast.Value) {
|
||||
fieldDef := value.Definition.Fields.ForName(child.Name)
|
||||
if fieldDef != nil {
|
||||
child.Value.ExpectedType = fieldDef.Type
|
||||
child.Value.ExpectedTypeHasDefault = fieldDef.DefaultValue != nil &&
|
||||
fieldDef.DefaultValue.Kind != ast.NullValue
|
||||
child.Value.Definition = w.Schema.Types[fieldDef.Type.Name()]
|
||||
}
|
||||
}
|
||||
@@ -208,6 +214,8 @@ func (w *Walker) walkValue(value *ast.Value) {
|
||||
func (w *Walker) walkArgument(argDef *ast.ArgumentDefinition, arg *ast.Argument) {
|
||||
if argDef != nil {
|
||||
arg.Value.ExpectedType = argDef.Type
|
||||
arg.Value.ExpectedTypeHasDefault = argDef.DefaultValue != nil &&
|
||||
argDef.DefaultValue.Kind != ast.NullValue
|
||||
arg.Value.Definition = w.Schema.Types[argDef.Type.Name()]
|
||||
}
|
||||
|
||||
|
||||
+19
-9
@@ -3,10 +3,8 @@ package rules
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/vektah/gqlparser/v2/ast"
|
||||
|
||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
||||
)
|
||||
@@ -17,12 +15,24 @@ func ruleFuncFieldsOnCorrectType(observers *Events, addError AddErrFunc, disable
|
||||
return
|
||||
}
|
||||
|
||||
message := fmt.Sprintf(`Cannot query field "%s" on type "%s".`, field.Name, field.ObjectDefinition.Name)
|
||||
message := fmt.Sprintf(
|
||||
`Cannot query field "%s" on type "%s".`,
|
||||
field.Name,
|
||||
field.ObjectDefinition.Name,
|
||||
)
|
||||
|
||||
if !disableSuggestion {
|
||||
if suggestedTypeNames := getSuggestedTypeNames(walker, field.ObjectDefinition, field.Name); suggestedTypeNames != nil {
|
||||
message += " Did you mean to use an inline fragment on " + QuotedOrList(suggestedTypeNames...) + "?"
|
||||
} else if suggestedFieldNames := getSuggestedFieldNames(field.ObjectDefinition, field.Name); suggestedFieldNames != nil {
|
||||
if suggestedTypeNames := getSuggestedTypeNames(
|
||||
walker,
|
||||
field.ObjectDefinition,
|
||||
field.Name,
|
||||
); suggestedTypeNames != nil {
|
||||
message += " Did you mean to use an inline fragment on " + QuotedOrList(
|
||||
suggestedTypeNames...) + "?"
|
||||
} else if suggestedFieldNames := getSuggestedFieldNames(
|
||||
field.ObjectDefinition,
|
||||
field.Name,
|
||||
); suggestedFieldNames != nil {
|
||||
message += " Did you mean " + QuotedOrList(suggestedFieldNames...) + "?"
|
||||
}
|
||||
}
|
||||
@@ -89,7 +99,7 @@ func getSuggestedTypeNames(walker *Walker, parent *ast.Definition, name string)
|
||||
if diff != 0 {
|
||||
return diff < 0
|
||||
}
|
||||
return strings.Compare(typeA, typeB) < 0
|
||||
return typeA < typeB
|
||||
})
|
||||
|
||||
return suggestedTypes
|
||||
@@ -99,8 +109,8 @@ func getSuggestedTypeNames(walker *Walker, parent *ast.Definition, name string)
|
||||
// where max is set to the slice’s length,
|
||||
// we ensure that appending elements results
|
||||
// in a slice backed by a distinct array.
|
||||
// This method prevents the shared array issue
|
||||
func concatSlice(first []string, second []string) []string {
|
||||
// This method prevents the shared array issue.
|
||||
func concatSlice(first, second []string) []string {
|
||||
n := len(first)
|
||||
return append(first[:n:n], second...)
|
||||
}
|
||||
|
||||
Generated
Vendored
+11
-4
@@ -4,7 +4,6 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/vektah/gqlparser/v2/ast"
|
||||
|
||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
||||
)
|
||||
@@ -18,7 +17,10 @@ var FragmentsOnCompositeTypesRule = Rule{
|
||||
return
|
||||
}
|
||||
|
||||
message := fmt.Sprintf(`Fragment cannot condition on non composite type "%s".`, inlineFragment.TypeCondition)
|
||||
message := fmt.Sprintf(
|
||||
`Fragment cannot condition on non composite type "%s".`,
|
||||
inlineFragment.TypeCondition,
|
||||
)
|
||||
|
||||
addError(
|
||||
Message("%s", message),
|
||||
@@ -27,11 +29,16 @@ var FragmentsOnCompositeTypesRule = Rule{
|
||||
})
|
||||
|
||||
observers.OnFragment(func(walker *Walker, fragment *ast.FragmentDefinition) {
|
||||
if fragment.Definition == nil || fragment.TypeCondition == "" || fragment.Definition.IsCompositeType() {
|
||||
if fragment.Definition == nil || fragment.TypeCondition == "" ||
|
||||
fragment.Definition.IsCompositeType() {
|
||||
return
|
||||
}
|
||||
|
||||
message := fmt.Sprintf(`Fragment "%s" cannot condition on non composite type "%s".`, fragment.Name, fragment.TypeCondition)
|
||||
message := fmt.Sprintf(
|
||||
`Fragment "%s" cannot condition on non composite type "%s".`,
|
||||
fragment.Name,
|
||||
fragment.TypeCondition,
|
||||
)
|
||||
|
||||
addError(
|
||||
Message("%s", message),
|
||||
|
||||
+12
-3
@@ -2,7 +2,6 @@ package rules
|
||||
|
||||
import (
|
||||
"github.com/vektah/gqlparser/v2/ast"
|
||||
|
||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
||||
)
|
||||
@@ -21,7 +20,12 @@ func ruleFuncKnownArgumentNames(observers *Events, addError AddErrFunc, disableS
|
||||
|
||||
if disableSuggestion {
|
||||
addError(
|
||||
Message(`Unknown argument "%s" on field "%s.%s".`, arg.Name, field.ObjectDefinition.Name, field.Name),
|
||||
Message(
|
||||
`Unknown argument "%s" on field "%s.%s".`,
|
||||
arg.Name,
|
||||
field.ObjectDefinition.Name,
|
||||
field.Name,
|
||||
),
|
||||
At(field.Position),
|
||||
)
|
||||
} else {
|
||||
@@ -30,7 +34,12 @@ func ruleFuncKnownArgumentNames(observers *Events, addError AddErrFunc, disableS
|
||||
suggestions = append(suggestions, argDef.Name)
|
||||
}
|
||||
addError(
|
||||
Message(`Unknown argument "%s" on field "%s.%s".`, arg.Name, field.ObjectDefinition.Name, field.Name),
|
||||
Message(
|
||||
`Unknown argument "%s" on field "%s.%s".`,
|
||||
arg.Name,
|
||||
field.ObjectDefinition.Name,
|
||||
field.Name,
|
||||
),
|
||||
SuggestListQuoted("Did you mean", arg.Name, suggestions),
|
||||
At(field.Position),
|
||||
)
|
||||
|
||||
+9
-6
@@ -1,8 +1,9 @@
|
||||
package rules
|
||||
|
||||
import (
|
||||
"github.com/vektah/gqlparser/v2/ast"
|
||||
"slices"
|
||||
|
||||
"github.com/vektah/gqlparser/v2/ast"
|
||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
||||
)
|
||||
@@ -25,10 +26,8 @@ var KnownDirectivesRule = Rule{
|
||||
return
|
||||
}
|
||||
|
||||
for _, loc := range directive.Definition.Locations {
|
||||
if loc == directive.Location {
|
||||
return
|
||||
}
|
||||
if slices.Contains(directive.Definition.Locations, directive.Location) {
|
||||
return
|
||||
}
|
||||
|
||||
// position must be exists if directive.Definition != nil
|
||||
@@ -40,7 +39,11 @@ var KnownDirectivesRule = Rule{
|
||||
|
||||
if !seen[tmp] {
|
||||
addError(
|
||||
Message(`Directive "@%s" may not be used on %s.`, directive.Name, directive.Location),
|
||||
Message(
|
||||
`Directive "@%s" may not be used on %s.`,
|
||||
directive.Name,
|
||||
directive.Location,
|
||||
),
|
||||
At(directive.Position),
|
||||
)
|
||||
seen[tmp] = true
|
||||
|
||||
-1
@@ -2,7 +2,6 @@ package rules
|
||||
|
||||
import (
|
||||
"github.com/vektah/gqlparser/v2/ast"
|
||||
|
||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
||||
)
|
||||
|
||||
-1
@@ -4,7 +4,6 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/vektah/gqlparser/v2/ast"
|
||||
|
||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
||||
)
|
||||
|
||||
-1
@@ -2,7 +2,6 @@ package rules
|
||||
|
||||
import (
|
||||
"github.com/vektah/gqlparser/v2/ast"
|
||||
|
||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
||||
)
|
||||
|
||||
Generated
Vendored
-1
@@ -2,7 +2,6 @@ package rules
|
||||
|
||||
import (
|
||||
"github.com/vektah/gqlparser/v2/ast"
|
||||
|
||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
||||
)
|
||||
|
||||
+10
-3
@@ -2,7 +2,6 @@ package rules
|
||||
|
||||
import (
|
||||
"github.com/vektah/gqlparser/v2/ast"
|
||||
|
||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
||||
)
|
||||
@@ -29,7 +28,11 @@ var MaxIntrospectionDepth = Rule{
|
||||
},
|
||||
}
|
||||
|
||||
func checkDepthSelectionSet(selectionSet ast.SelectionSet, visitedFragments map[string]bool, depth int) bool {
|
||||
func checkDepthSelectionSet(
|
||||
selectionSet ast.SelectionSet,
|
||||
visitedFragments map[string]bool,
|
||||
depth int,
|
||||
) bool {
|
||||
for _, child := range selectionSet {
|
||||
if field, ok := child.(*ast.Field); ok {
|
||||
if checkDepthField(field, visitedFragments, depth) {
|
||||
@@ -63,7 +66,11 @@ func checkDepthField(field *ast.Field, visitedFragments map[string]bool, depth i
|
||||
return checkDepthSelectionSet(field.SelectionSet, visitedFragments, depth)
|
||||
}
|
||||
|
||||
func checkDepthFragmentSpread(fragmentSpread *ast.FragmentSpread, visitedFragments map[string]bool, depth int) bool {
|
||||
func checkDepthFragmentSpread(
|
||||
fragmentSpread *ast.FragmentSpread,
|
||||
visitedFragments map[string]bool,
|
||||
depth int,
|
||||
) bool {
|
||||
fragmentName := fragmentSpread.Name
|
||||
if visited, ok := visitedFragments[fragmentName]; ok && visited {
|
||||
// Fragment cycles are handled by `NoFragmentCyclesRule`.
|
||||
|
||||
+5
-2
@@ -5,7 +5,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/vektah/gqlparser/v2/ast"
|
||||
|
||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
||||
)
|
||||
@@ -55,7 +54,11 @@ var NoFragmentCyclesRule = Rule{
|
||||
via = fmt.Sprintf(" via %s", strings.Join(fragmentNames, ", "))
|
||||
}
|
||||
addError(
|
||||
Message(`Cannot spread fragment "%s" within itself%s.`, spreadName, via),
|
||||
Message(
|
||||
`Cannot spread fragment "%s" within itself%s.`,
|
||||
spreadName,
|
||||
via,
|
||||
),
|
||||
At(spreadNode.Position),
|
||||
)
|
||||
}
|
||||
|
||||
+7
-3
@@ -2,7 +2,6 @@ package rules
|
||||
|
||||
import (
|
||||
"github.com/vektah/gqlparser/v2/ast"
|
||||
|
||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
||||
)
|
||||
@@ -11,13 +10,18 @@ var NoUndefinedVariablesRule = Rule{
|
||||
Name: "NoUndefinedVariables",
|
||||
RuleFunc: func(observers *Events, addError AddErrFunc) {
|
||||
observers.OnValue(func(walker *Walker, value *ast.Value) {
|
||||
if walker.CurrentOperation == nil || value.Kind != ast.Variable || value.VariableDefinition != nil {
|
||||
if walker.CurrentOperation == nil || value.Kind != ast.Variable ||
|
||||
value.VariableDefinition != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if walker.CurrentOperation.Name != "" {
|
||||
addError(
|
||||
Message(`Variable "%s" is not defined by operation "%s".`, value, walker.CurrentOperation.Name),
|
||||
Message(
|
||||
`Variable "%s" is not defined by operation "%s".`,
|
||||
value,
|
||||
walker.CurrentOperation.Name,
|
||||
),
|
||||
At(value.Position),
|
||||
)
|
||||
} else {
|
||||
|
||||
-1
@@ -2,7 +2,6 @@ package rules
|
||||
|
||||
import (
|
||||
"github.com/vektah/gqlparser/v2/ast"
|
||||
|
||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
||||
)
|
||||
|
||||
+5
-2
@@ -2,7 +2,6 @@ package rules
|
||||
|
||||
import (
|
||||
"github.com/vektah/gqlparser/v2/ast"
|
||||
|
||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
||||
)
|
||||
@@ -18,7 +17,11 @@ var NoUnusedVariablesRule = Rule{
|
||||
|
||||
if operation.Name != "" {
|
||||
addError(
|
||||
Message(`Variable "$%s" is never used in operation "%s".`, varDef.Variable, operation.Name),
|
||||
Message(
|
||||
`Variable "$%s" is never used in operation "%s".`,
|
||||
varDef.Variable,
|
||||
operation.Name,
|
||||
),
|
||||
At(varDef.Position),
|
||||
)
|
||||
} else {
|
||||
|
||||
Generated
Vendored
+109
-29
@@ -6,7 +6,6 @@ import (
|
||||
"reflect"
|
||||
|
||||
"github.com/vektah/gqlparser/v2/ast"
|
||||
|
||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
||||
)
|
||||
@@ -82,7 +81,8 @@ var OverlappingFieldsCanBeMergedRule = Rule{
|
||||
})
|
||||
observers.OnField(func(walker *Walker, field *ast.Field) {
|
||||
if walker.CurrentOperation == nil {
|
||||
// When checking both Operation and Fragment, errors are duplicated when processing FragmentDefinition referenced from Operation
|
||||
// When checking both Operation and Fragment, errors are duplicated when processing
|
||||
// FragmentDefinition referenced from Operation
|
||||
return
|
||||
}
|
||||
m.walker = walker
|
||||
@@ -112,7 +112,11 @@ type pairSet struct {
|
||||
data map[string]map[string]bool
|
||||
}
|
||||
|
||||
func (pairSet *pairSet) Add(a *ast.FragmentSpread, b *ast.FragmentSpread, areMutuallyExclusive bool) {
|
||||
func (pairSet *pairSet) Add(
|
||||
a *ast.FragmentSpread,
|
||||
b *ast.FragmentSpread,
|
||||
areMutuallyExclusive bool,
|
||||
) {
|
||||
add := func(a *ast.FragmentSpread, b *ast.FragmentSpread) {
|
||||
m := pairSet.data[a.Name]
|
||||
if m == nil {
|
||||
@@ -125,7 +129,11 @@ func (pairSet *pairSet) Add(a *ast.FragmentSpread, b *ast.FragmentSpread, areMut
|
||||
add(b, a)
|
||||
}
|
||||
|
||||
func (pairSet *pairSet) Has(a *ast.FragmentSpread, b *ast.FragmentSpread, areMutuallyExclusive bool) bool {
|
||||
func (pairSet *pairSet) Has(
|
||||
a *ast.FragmentSpread,
|
||||
b *ast.FragmentSpread,
|
||||
areMutuallyExclusive bool,
|
||||
) bool {
|
||||
am, ok := pairSet.data[a.Name]
|
||||
if !ok {
|
||||
return false
|
||||
@@ -224,7 +232,11 @@ func (m *ConflictMessage) addFieldsConflictMessage(addError AddErrFunc) {
|
||||
var buf bytes.Buffer
|
||||
m.String(&buf)
|
||||
addError(
|
||||
Message(`Fields "%s" conflict because %s. Use different aliases on the fields to fetch both if this was intentional.`, m.ResponseName, buf.String()),
|
||||
Message(
|
||||
`Fields "%s" conflict because %s. Use different aliases on the fields to fetch both if this was intentional.`,
|
||||
m.ResponseName,
|
||||
buf.String(),
|
||||
),
|
||||
At(m.Position),
|
||||
)
|
||||
}
|
||||
@@ -240,7 +252,9 @@ type overlappingFieldsCanBeMergedManager struct {
|
||||
comparedFragments map[string]bool
|
||||
}
|
||||
|
||||
func (m *overlappingFieldsCanBeMergedManager) findConflictsWithinSelectionSet(selectionSet ast.SelectionSet) []*ConflictMessage {
|
||||
func (m *overlappingFieldsCanBeMergedManager) findConflictsWithinSelectionSet(
|
||||
selectionSet ast.SelectionSet,
|
||||
) []*ConflictMessage {
|
||||
if len(selectionSet) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -271,7 +285,12 @@ func (m *overlappingFieldsCanBeMergedManager) findConflictsWithinSelectionSet(se
|
||||
return conflicts.Conflicts
|
||||
}
|
||||
|
||||
func (m *overlappingFieldsCanBeMergedManager) collectConflictsBetweenFieldsAndFragment(conflicts *conflictMessageContainer, areMutuallyExclusive bool, fieldsMap *sequentialFieldsMap, fragmentSpread *ast.FragmentSpread) {
|
||||
func (m *overlappingFieldsCanBeMergedManager) collectConflictsBetweenFieldsAndFragment(
|
||||
conflicts *conflictMessageContainer,
|
||||
areMutuallyExclusive bool,
|
||||
fieldsMap *sequentialFieldsMap,
|
||||
fragmentSpread *ast.FragmentSpread,
|
||||
) {
|
||||
if m.comparedFragments[fragmentSpread.Name] {
|
||||
return
|
||||
}
|
||||
@@ -299,11 +318,21 @@ func (m *overlappingFieldsCanBeMergedManager) collectConflictsBetweenFieldsAndFr
|
||||
if fragmentSpread.Name == baseFragmentSpread.Name {
|
||||
continue
|
||||
}
|
||||
m.collectConflictsBetweenFieldsAndFragment(conflicts, areMutuallyExclusive, fieldsMap, fragmentSpread)
|
||||
m.collectConflictsBetweenFieldsAndFragment(
|
||||
conflicts,
|
||||
areMutuallyExclusive,
|
||||
fieldsMap,
|
||||
fragmentSpread,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *overlappingFieldsCanBeMergedManager) collectConflictsBetweenFragments(conflicts *conflictMessageContainer, areMutuallyExclusive bool, fragmentSpreadA *ast.FragmentSpread, fragmentSpreadB *ast.FragmentSpread) {
|
||||
func (m *overlappingFieldsCanBeMergedManager) collectConflictsBetweenFragments(
|
||||
conflicts *conflictMessageContainer,
|
||||
areMutuallyExclusive bool,
|
||||
fragmentSpreadA *ast.FragmentSpread,
|
||||
fragmentSpreadB *ast.FragmentSpread,
|
||||
) {
|
||||
var check func(fragmentSpreadA *ast.FragmentSpread, fragmentSpreadB *ast.FragmentSpread)
|
||||
check = func(fragmentSpreadA *ast.FragmentSpread, fragmentSpreadB *ast.FragmentSpread) {
|
||||
if fragmentSpreadA.Name == fragmentSpreadB.Name {
|
||||
@@ -322,8 +351,12 @@ func (m *overlappingFieldsCanBeMergedManager) collectConflictsBetweenFragments(c
|
||||
return
|
||||
}
|
||||
|
||||
fieldsMapA, fragmentSpreadsA := getFieldsAndFragmentNames(fragmentSpreadA.Definition.SelectionSet)
|
||||
fieldsMapB, fragmentSpreadsB := getFieldsAndFragmentNames(fragmentSpreadB.Definition.SelectionSet)
|
||||
fieldsMapA, fragmentSpreadsA := getFieldsAndFragmentNames(
|
||||
fragmentSpreadA.Definition.SelectionSet,
|
||||
)
|
||||
fieldsMapB, fragmentSpreadsB := getFieldsAndFragmentNames(
|
||||
fragmentSpreadB.Definition.SelectionSet,
|
||||
)
|
||||
|
||||
// (F) First, collect all conflicts between these two collections of fields
|
||||
// (not including any nested fragments).
|
||||
@@ -344,7 +377,11 @@ func (m *overlappingFieldsCanBeMergedManager) collectConflictsBetweenFragments(c
|
||||
check(fragmentSpreadA, fragmentSpreadB)
|
||||
}
|
||||
|
||||
func (m *overlappingFieldsCanBeMergedManager) findConflictsBetweenSubSelectionSets(areMutuallyExclusive bool, selectionSetA ast.SelectionSet, selectionSetB ast.SelectionSet) *conflictMessageContainer {
|
||||
func (m *overlappingFieldsCanBeMergedManager) findConflictsBetweenSubSelectionSets(
|
||||
areMutuallyExclusive bool,
|
||||
selectionSetA ast.SelectionSet,
|
||||
selectionSetB ast.SelectionSet,
|
||||
) *conflictMessageContainer {
|
||||
var conflicts conflictMessageContainer
|
||||
|
||||
fieldsMapA, fragmentSpreadsA := getFieldsAndFragmentNames(selectionSetA)
|
||||
@@ -357,14 +394,24 @@ func (m *overlappingFieldsCanBeMergedManager) findConflictsBetweenSubSelectionSe
|
||||
// those referenced by each fragment name associated with the second.
|
||||
for _, fragmentSpread := range fragmentSpreadsB {
|
||||
m.comparedFragments = make(map[string]bool)
|
||||
m.collectConflictsBetweenFieldsAndFragment(&conflicts, areMutuallyExclusive, fieldsMapA, fragmentSpread)
|
||||
m.collectConflictsBetweenFieldsAndFragment(
|
||||
&conflicts,
|
||||
areMutuallyExclusive,
|
||||
fieldsMapA,
|
||||
fragmentSpread,
|
||||
)
|
||||
}
|
||||
|
||||
// (I) Then collect conflicts between the second collection of fields and
|
||||
// those referenced by each fragment name associated with the first.
|
||||
for _, fragmentSpread := range fragmentSpreadsA {
|
||||
m.comparedFragments = make(map[string]bool)
|
||||
m.collectConflictsBetweenFieldsAndFragment(&conflicts, areMutuallyExclusive, fieldsMapB, fragmentSpread)
|
||||
m.collectConflictsBetweenFieldsAndFragment(
|
||||
&conflicts,
|
||||
areMutuallyExclusive,
|
||||
fieldsMapB,
|
||||
fragmentSpread,
|
||||
)
|
||||
}
|
||||
|
||||
// (J) Also collect conflicts between any fragment names by the first and
|
||||
@@ -372,7 +419,12 @@ func (m *overlappingFieldsCanBeMergedManager) findConflictsBetweenSubSelectionSe
|
||||
// names to each item in the second set of names.
|
||||
for _, fragmentSpreadA := range fragmentSpreadsA {
|
||||
for _, fragmentSpreadB := range fragmentSpreadsB {
|
||||
m.collectConflictsBetweenFragments(&conflicts, areMutuallyExclusive, fragmentSpreadA, fragmentSpreadB)
|
||||
m.collectConflictsBetweenFragments(
|
||||
&conflicts,
|
||||
areMutuallyExclusive,
|
||||
fragmentSpreadA,
|
||||
fragmentSpreadB,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -383,7 +435,10 @@ func (m *overlappingFieldsCanBeMergedManager) findConflictsBetweenSubSelectionSe
|
||||
return &conflicts
|
||||
}
|
||||
|
||||
func (m *overlappingFieldsCanBeMergedManager) collectConflictsWithin(conflicts *conflictMessageContainer, fieldsMap *sequentialFieldsMap) {
|
||||
func (m *overlappingFieldsCanBeMergedManager) collectConflictsWithin(
|
||||
conflicts *conflictMessageContainer,
|
||||
fieldsMap *sequentialFieldsMap,
|
||||
) {
|
||||
for _, fields := range fieldsMap.Iterator() {
|
||||
for idx, fieldA := range fields {
|
||||
for _, fieldB := range fields[idx+1:] {
|
||||
@@ -396,7 +451,12 @@ func (m *overlappingFieldsCanBeMergedManager) collectConflictsWithin(conflicts *
|
||||
}
|
||||
}
|
||||
|
||||
func (m *overlappingFieldsCanBeMergedManager) collectConflictsBetween(conflicts *conflictMessageContainer, parentFieldsAreMutuallyExclusive bool, fieldsMapA *sequentialFieldsMap, fieldsMapB *sequentialFieldsMap) {
|
||||
func (m *overlappingFieldsCanBeMergedManager) collectConflictsBetween(
|
||||
conflicts *conflictMessageContainer,
|
||||
parentFieldsAreMutuallyExclusive bool,
|
||||
fieldsMapA *sequentialFieldsMap,
|
||||
fieldsMapB *sequentialFieldsMap,
|
||||
) {
|
||||
for _, fieldsEntryA := range fieldsMapA.KeyValueIterator() {
|
||||
fieldsB, ok := fieldsMapB.Get(fieldsEntryA.ResponseName)
|
||||
if !ok {
|
||||
@@ -413,7 +473,11 @@ func (m *overlappingFieldsCanBeMergedManager) collectConflictsBetween(conflicts
|
||||
}
|
||||
}
|
||||
|
||||
func (m *overlappingFieldsCanBeMergedManager) findConflict(parentFieldsAreMutuallyExclusive bool, fieldA *ast.Field, fieldB *ast.Field) *ConflictMessage {
|
||||
func (m *overlappingFieldsCanBeMergedManager) findConflict(
|
||||
parentFieldsAreMutuallyExclusive bool,
|
||||
fieldA *ast.Field,
|
||||
fieldB *ast.Field,
|
||||
) *ConflictMessage {
|
||||
if fieldA.ObjectDefinition == nil || fieldB.ObjectDefinition == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -437,8 +501,12 @@ func (m *overlappingFieldsCanBeMergedManager) findConflict(parentFieldsAreMutual
|
||||
if fieldA.Name != fieldB.Name {
|
||||
return &ConflictMessage{
|
||||
ResponseName: fieldNameA,
|
||||
Message: fmt.Sprintf(`"%s" and "%s" are different fields`, fieldA.Name, fieldB.Name),
|
||||
Position: fieldB.Position,
|
||||
Message: fmt.Sprintf(
|
||||
`"%s" and "%s" are different fields`,
|
||||
fieldA.Name,
|
||||
fieldB.Name,
|
||||
),
|
||||
Position: fieldB.Position,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -452,18 +520,27 @@ func (m *overlappingFieldsCanBeMergedManager) findConflict(parentFieldsAreMutual
|
||||
}
|
||||
}
|
||||
|
||||
if fieldA.Definition != nil && fieldB.Definition != nil && doTypesConflict(m.walker, fieldA.Definition.Type, fieldB.Definition.Type) {
|
||||
if fieldA.Definition != nil && fieldB.Definition != nil &&
|
||||
doTypesConflict(m.walker, fieldA.Definition.Type, fieldB.Definition.Type) {
|
||||
return &ConflictMessage{
|
||||
ResponseName: fieldNameA,
|
||||
Message: fmt.Sprintf(`they return conflicting types "%s" and "%s"`, fieldA.Definition.Type.String(), fieldB.Definition.Type.String()),
|
||||
Position: fieldB.Position,
|
||||
Message: fmt.Sprintf(
|
||||
`they return conflicting types "%s" and "%s"`,
|
||||
fieldA.Definition.Type.String(),
|
||||
fieldB.Definition.Type.String(),
|
||||
),
|
||||
Position: fieldB.Position,
|
||||
}
|
||||
}
|
||||
|
||||
// Collect and compare sub-fields. Use the same "visited fragment names" list
|
||||
// for both collections so fields in a fragment reference are never
|
||||
// compared to themselves.
|
||||
conflicts := m.findConflictsBetweenSubSelectionSets(areMutuallyExclusive, fieldA.SelectionSet, fieldB.SelectionSet)
|
||||
conflicts := m.findConflictsBetweenSubSelectionSets(
|
||||
areMutuallyExclusive,
|
||||
fieldA.SelectionSet,
|
||||
fieldB.SelectionSet,
|
||||
)
|
||||
if conflicts == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -474,7 +551,7 @@ func (m *overlappingFieldsCanBeMergedManager) findConflict(parentFieldsAreMutual
|
||||
}
|
||||
}
|
||||
|
||||
func sameArguments(args1 []*ast.Argument, args2 []*ast.Argument) bool {
|
||||
func sameArguments(args1, args2 []*ast.Argument) bool {
|
||||
if len(args1) != len(args2) {
|
||||
return false
|
||||
}
|
||||
@@ -493,7 +570,7 @@ func sameArguments(args1 []*ast.Argument, args2 []*ast.Argument) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func sameValue(value1 *ast.Value, value2 *ast.Value) bool {
|
||||
func sameValue(value1, value2 *ast.Value) bool {
|
||||
if value1.Kind != value2.Kind {
|
||||
return false
|
||||
}
|
||||
@@ -503,7 +580,7 @@ func sameValue(value1 *ast.Value, value2 *ast.Value) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func doTypesConflict(walker *Walker, type1 *ast.Type, type2 *ast.Type) bool {
|
||||
func doTypesConflict(walker *Walker, type1, type2 *ast.Type) bool {
|
||||
if type1.Elem != nil {
|
||||
if type2.Elem != nil {
|
||||
return doTypesConflict(walker, type1.Elem, type2.Elem)
|
||||
@@ -522,14 +599,17 @@ func doTypesConflict(walker *Walker, type1 *ast.Type, type2 *ast.Type) bool {
|
||||
|
||||
t1 := walker.Schema.Types[type1.NamedType]
|
||||
t2 := walker.Schema.Types[type2.NamedType]
|
||||
if (t1.Kind == ast.Scalar || t1.Kind == ast.Enum) && (t2.Kind == ast.Scalar || t2.Kind == ast.Enum) {
|
||||
if (t1.Kind == ast.Scalar || t1.Kind == ast.Enum) &&
|
||||
(t2.Kind == ast.Scalar || t2.Kind == ast.Enum) {
|
||||
return t1.Name != t2.Name
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func getFieldsAndFragmentNames(selectionSet ast.SelectionSet) (*sequentialFieldsMap, []*ast.FragmentSpread) {
|
||||
func getFieldsAndFragmentNames(
|
||||
selectionSet ast.SelectionSet,
|
||||
) (*sequentialFieldsMap, []*ast.FragmentSpread) {
|
||||
fieldsMap := sequentialFieldsMap{
|
||||
data: make(map[string][]*ast.Field),
|
||||
}
|
||||
|
||||
Generated
Vendored
+21
-8
@@ -2,7 +2,6 @@ package rules
|
||||
|
||||
import (
|
||||
"github.com/vektah/gqlparser/v2/ast"
|
||||
|
||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
||||
)
|
||||
@@ -49,7 +48,11 @@ var PossibleFragmentSpreadsRule = Rule{
|
||||
observers.OnInlineFragment(func(walker *Walker, inlineFragment *ast.InlineFragment) {
|
||||
validate(walker, inlineFragment.ObjectDefinition, inlineFragment.TypeCondition, func() {
|
||||
addError(
|
||||
Message(`Fragment cannot be spread here as objects of type "%s" can never be of type "%s".`, inlineFragment.ObjectDefinition.Name, inlineFragment.TypeCondition),
|
||||
Message(
|
||||
`Fragment cannot be spread here as objects of type "%s" can never be of type "%s".`,
|
||||
inlineFragment.ObjectDefinition.Name,
|
||||
inlineFragment.TypeCondition,
|
||||
),
|
||||
At(inlineFragment.Position),
|
||||
)
|
||||
})
|
||||
@@ -59,12 +62,22 @@ var PossibleFragmentSpreadsRule = Rule{
|
||||
if fragmentSpread.Definition == nil {
|
||||
return
|
||||
}
|
||||
validate(walker, fragmentSpread.ObjectDefinition, fragmentSpread.Definition.TypeCondition, func() {
|
||||
addError(
|
||||
Message(`Fragment "%s" cannot be spread here as objects of type "%s" can never be of type "%s".`, fragmentSpread.Name, fragmentSpread.ObjectDefinition.Name, fragmentSpread.Definition.TypeCondition),
|
||||
At(fragmentSpread.Position),
|
||||
)
|
||||
})
|
||||
validate(
|
||||
walker,
|
||||
fragmentSpread.ObjectDefinition,
|
||||
fragmentSpread.Definition.TypeCondition,
|
||||
func() {
|
||||
addError(
|
||||
Message(
|
||||
`Fragment "%s" cannot be spread here as objects of type "%s" can never be of type "%s".`,
|
||||
fragmentSpread.Name,
|
||||
fragmentSpread.ObjectDefinition.Name,
|
||||
fragmentSpread.Definition.TypeCondition,
|
||||
),
|
||||
At(fragmentSpread.Position),
|
||||
)
|
||||
},
|
||||
)
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
+8
-1
@@ -77,6 +77,7 @@ func (r *Rules) AddRule(name string, ruleFunc core.RuleFunc) {
|
||||
|
||||
// GetInner returns the internal rule map.
|
||||
// If the map is not initialized, it returns an empty map.
|
||||
// This returns a copy of the rules map, not the original map.
|
||||
func (r *Rules) GetInner() map[string]core.RuleFunc {
|
||||
if r == nil {
|
||||
return nil // impossible nonsense, hopefully
|
||||
@@ -84,7 +85,13 @@ func (r *Rules) GetInner() map[string]core.RuleFunc {
|
||||
if r.rules == nil {
|
||||
return make(map[string]core.RuleFunc)
|
||||
}
|
||||
return r.rules
|
||||
|
||||
rules := make(map[string]core.RuleFunc)
|
||||
for k, v := range r.rules {
|
||||
rules[k] = v
|
||||
}
|
||||
|
||||
return rules
|
||||
}
|
||||
|
||||
// RemoveRule removes a rule with the specified name from the rule set.
|
||||
|
||||
+10
-3
@@ -2,7 +2,6 @@ package rules
|
||||
|
||||
import (
|
||||
"github.com/vektah/gqlparser/v2/ast"
|
||||
|
||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
||||
)
|
||||
@@ -22,14 +21,22 @@ var ScalarLeafsRule = Rule{
|
||||
|
||||
if fieldType.IsLeafType() && len(field.SelectionSet) > 0 {
|
||||
addError(
|
||||
Message(`Field "%s" must not have a selection since type "%s" has no subfields.`, field.Name, fieldType.Name),
|
||||
Message(
|
||||
`Field "%s" must not have a selection since type "%s" has no subfields.`,
|
||||
field.Name,
|
||||
fieldType.Name,
|
||||
),
|
||||
At(field.Position),
|
||||
)
|
||||
}
|
||||
|
||||
if !fieldType.IsLeafType() && len(field.SelectionSet) == 0 {
|
||||
addError(
|
||||
Message(`Field "%s" of type "%s" must have a selection of subfields.`, field.Name, field.Definition.Type.String()),
|
||||
Message(
|
||||
`Field "%s" of type "%s" must have a selection of subfields.`,
|
||||
field.Name,
|
||||
field.Definition.Type.String(),
|
||||
),
|
||||
Suggestf(`"%s { ... }"`, field.Name),
|
||||
At(field.Position),
|
||||
)
|
||||
|
||||
Generated
Vendored
-1
@@ -5,7 +5,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/vektah/gqlparser/v2/ast"
|
||||
|
||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
||||
)
|
||||
|
||||
-1
@@ -2,7 +2,6 @@ package rules
|
||||
|
||||
import (
|
||||
"github.com/vektah/gqlparser/v2/ast"
|
||||
|
||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
||||
)
|
||||
|
||||
Generated
Vendored
+4
-2
@@ -2,7 +2,6 @@ package rules
|
||||
|
||||
import (
|
||||
"github.com/vektah/gqlparser/v2/ast"
|
||||
|
||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
||||
)
|
||||
@@ -16,7 +15,10 @@ var UniqueDirectivesPerLocationRule = Rule{
|
||||
for _, dir := range directives {
|
||||
if dir.Name != "repeatable" && seen[dir.Name] {
|
||||
addError(
|
||||
Message(`The directive "@%s" can only be used once at this location.`, dir.Name),
|
||||
Message(
|
||||
`The directive "@%s" can only be used once at this location.`,
|
||||
dir.Name,
|
||||
),
|
||||
At(dir.Position),
|
||||
)
|
||||
}
|
||||
|
||||
-1
@@ -2,7 +2,6 @@ package rules
|
||||
|
||||
import (
|
||||
"github.com/vektah/gqlparser/v2/ast"
|
||||
|
||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
||||
)
|
||||
|
||||
Generated
Vendored
-1
@@ -2,7 +2,6 @@ package rules
|
||||
|
||||
import (
|
||||
"github.com/vektah/gqlparser/v2/ast"
|
||||
|
||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
||||
)
|
||||
|
||||
-1
@@ -2,7 +2,6 @@ package rules
|
||||
|
||||
import (
|
||||
"github.com/vektah/gqlparser/v2/ast"
|
||||
|
||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
||||
)
|
||||
|
||||
-1
@@ -2,7 +2,6 @@ package rules
|
||||
|
||||
import (
|
||||
"github.com/vektah/gqlparser/v2/ast"
|
||||
|
||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
||||
)
|
||||
|
||||
+78
-17
@@ -6,7 +6,6 @@ import (
|
||||
"strconv"
|
||||
|
||||
"github.com/vektah/gqlparser/v2/ast"
|
||||
|
||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
||||
)
|
||||
@@ -19,7 +18,11 @@ func ruleFuncValuesOfCorrectType(observers *Events, addError AddErrFunc, disable
|
||||
|
||||
if value.Kind == ast.NullValue && value.ExpectedType.NonNull {
|
||||
addError(
|
||||
Message(`Expected value of type "%s", found %s.`, value.ExpectedType.String(), value.String()),
|
||||
Message(
|
||||
`Expected value of type "%s", found %s.`,
|
||||
value.ExpectedType.String(),
|
||||
value.String(),
|
||||
),
|
||||
At(value.Position),
|
||||
)
|
||||
}
|
||||
@@ -66,13 +69,21 @@ func ruleFuncValuesOfCorrectType(observers *Events, addError AddErrFunc, disable
|
||||
if value.Definition.Kind == ast.Enum {
|
||||
if disableSuggestion {
|
||||
addError(
|
||||
Message(`Enum "%s" cannot represent non-enum value: %s.`, value.ExpectedType.String(), value.String()),
|
||||
Message(
|
||||
`Enum "%s" cannot represent non-enum value: %s.`,
|
||||
value.ExpectedType.String(),
|
||||
value.String(),
|
||||
),
|
||||
At(value.Position),
|
||||
)
|
||||
} else {
|
||||
rawValStr := fmt.Sprint(rawVal)
|
||||
addError(
|
||||
Message(`Enum "%s" cannot represent non-enum value: %s.`, value.ExpectedType.String(), value.String()),
|
||||
Message(
|
||||
`Enum "%s" cannot represent non-enum value: %s.`,
|
||||
value.ExpectedType.String(),
|
||||
value.String(),
|
||||
),
|
||||
SuggestListQuoted("Did you mean the enum value", rawValStr, possibleEnums),
|
||||
At(value.Position),
|
||||
)
|
||||
@@ -92,20 +103,32 @@ func ruleFuncValuesOfCorrectType(observers *Events, addError AddErrFunc, disable
|
||||
rawValStr := fmt.Sprint(rawVal)
|
||||
addError(
|
||||
unexpectedTypeMessageOnly(value),
|
||||
SuggestListUnquoted("Did you mean the enum value", rawValStr, possibleEnums),
|
||||
SuggestListUnquoted(
|
||||
"Did you mean the enum value",
|
||||
rawValStr,
|
||||
possibleEnums,
|
||||
),
|
||||
At(value.Position),
|
||||
)
|
||||
}
|
||||
} else if value.Definition.EnumValues.ForName(value.Raw) == nil {
|
||||
if disableSuggestion {
|
||||
addError(
|
||||
Message(`Value "%s" does not exist in "%s" enum.`, value.String(), value.ExpectedType.String()),
|
||||
Message(
|
||||
`Value "%s" does not exist in "%s" enum.`,
|
||||
value.String(),
|
||||
value.ExpectedType.String(),
|
||||
),
|
||||
At(value.Position),
|
||||
)
|
||||
} else {
|
||||
rawValStr := fmt.Sprint(rawVal)
|
||||
addError(
|
||||
Message(`Value "%s" does not exist in "%s" enum.`, value.String(), value.ExpectedType.String()),
|
||||
Message(
|
||||
`Value "%s" does not exist in "%s" enum.`,
|
||||
value.String(),
|
||||
value.ExpectedType.String(),
|
||||
),
|
||||
SuggestListQuoted("Did you mean the enum value", rawValStr, possibleEnums),
|
||||
At(value.Position),
|
||||
)
|
||||
@@ -124,7 +147,12 @@ func ruleFuncValuesOfCorrectType(observers *Events, addError AddErrFunc, disable
|
||||
fieldValue := value.Children.ForName(field.Name)
|
||||
if fieldValue == nil && field.DefaultValue == nil {
|
||||
addError(
|
||||
Message(`Field "%s.%s" of required type "%s" was not provided.`, value.Definition.Name, field.Name, field.Type.String()),
|
||||
Message(
|
||||
`Field "%s.%s" of required type "%s" was not provided.`,
|
||||
value.Definition.Name,
|
||||
field.Name,
|
||||
field.Type.String(),
|
||||
),
|
||||
At(value.Position),
|
||||
)
|
||||
continue
|
||||
@@ -137,7 +165,10 @@ func ruleFuncValuesOfCorrectType(observers *Events, addError AddErrFunc, disable
|
||||
func() {
|
||||
if len(value.Children) != 1 {
|
||||
addError(
|
||||
Message(`OneOf Input Object "%s" must specify exactly one key.`, value.Definition.Name),
|
||||
Message(
|
||||
`OneOf Input Object "%s" must specify exactly one key.`,
|
||||
value.Definition.Name,
|
||||
),
|
||||
At(value.Position),
|
||||
)
|
||||
return
|
||||
@@ -147,7 +178,11 @@ func ruleFuncValuesOfCorrectType(observers *Events, addError AddErrFunc, disable
|
||||
isNullLiteral := fieldValue == nil || fieldValue.Kind == ast.NullValue
|
||||
if isNullLiteral {
|
||||
addError(
|
||||
Message(`Field "%s.%s" must be non-null.`, value.Definition.Name, value.Definition.Fields[0].Name),
|
||||
Message(
|
||||
`Field "%s.%s" must be non-null.`,
|
||||
value.Definition.Name,
|
||||
value.Definition.Fields[0].Name,
|
||||
),
|
||||
At(fieldValue.Position),
|
||||
)
|
||||
return
|
||||
@@ -159,7 +194,11 @@ func ruleFuncValuesOfCorrectType(observers *Events, addError AddErrFunc, disable
|
||||
isNullableVariable := !fieldValue.VariableDefinition.Type.NonNull
|
||||
if isNullableVariable {
|
||||
addError(
|
||||
Message(`Variable "%s" must be non-nullable to be used for OneOf Input Object "%s".`, variableName, value.Definition.Name),
|
||||
Message(
|
||||
`Variable "%s" must be non-nullable to be used for OneOf Input Object "%s".`,
|
||||
variableName,
|
||||
value.Definition.Name,
|
||||
),
|
||||
At(fieldValue.Position),
|
||||
)
|
||||
}
|
||||
@@ -172,7 +211,11 @@ func ruleFuncValuesOfCorrectType(observers *Events, addError AddErrFunc, disable
|
||||
if value.Definition.Fields.ForName(fieldValue.Name) == nil {
|
||||
if disableSuggestion {
|
||||
addError(
|
||||
Message(`Field "%s" is not defined by type "%s".`, fieldValue.Name, value.Definition.Name),
|
||||
Message(
|
||||
`Field "%s" is not defined by type "%s".`,
|
||||
fieldValue.Name,
|
||||
value.Definition.Name,
|
||||
),
|
||||
At(fieldValue.Position),
|
||||
)
|
||||
} else {
|
||||
@@ -182,7 +225,11 @@ func ruleFuncValuesOfCorrectType(observers *Events, addError AddErrFunc, disable
|
||||
}
|
||||
|
||||
addError(
|
||||
Message(`Field "%s" is not defined by type "%s".`, fieldValue.Name, value.Definition.Name),
|
||||
Message(
|
||||
`Field "%s" is not defined by type "%s".`,
|
||||
fieldValue.Name,
|
||||
value.Definition.Name,
|
||||
),
|
||||
SuggestListQuoted("Did you mean", fieldValue.Name, suggestions),
|
||||
At(fieldValue.Position),
|
||||
)
|
||||
@@ -223,7 +270,12 @@ func unexpectedTypeMessage(addError AddErrFunc, v *ast.Value) {
|
||||
func unexpectedTypeMessageOnly(v *ast.Value) ErrorOption {
|
||||
switch v.ExpectedType.String() {
|
||||
case "Int", "Int!":
|
||||
if _, err := strconv.ParseInt(v.Raw, 10, 32); err != nil && errors.Is(err, strconv.ErrRange) {
|
||||
if _, err := strconv.ParseInt(
|
||||
v.Raw,
|
||||
10,
|
||||
32,
|
||||
); err != nil &&
|
||||
errors.Is(err, strconv.ErrRange) {
|
||||
return Message(`Int cannot represent non 32-bit signed integer value: %s`, v.String())
|
||||
}
|
||||
return Message(`Int cannot represent non-integer value: %s`, v.String())
|
||||
@@ -236,11 +288,20 @@ func unexpectedTypeMessageOnly(v *ast.Value) ErrorOption {
|
||||
case "ID", "ID!":
|
||||
return Message(`ID cannot represent a non-string and non-integer value: %s`, v.String())
|
||||
// case "Enum":
|
||||
// return Message(`Enum "%s" cannot represent non-enum value: %s`, v.ExpectedType.String(), v.String())
|
||||
// return Message(`Enum "%s" cannot represent non-enum value: %s`, v.ExpectedType.String(),
|
||||
// v.String())
|
||||
default:
|
||||
if v.Definition.Kind == ast.Enum {
|
||||
return Message(`Enum "%s" cannot represent non-enum value: %s.`, v.ExpectedType.String(), v.String())
|
||||
return Message(
|
||||
`Enum "%s" cannot represent non-enum value: %s.`,
|
||||
v.ExpectedType.String(),
|
||||
v.String(),
|
||||
)
|
||||
}
|
||||
return Message(`Expected value of type "%s", found %s.`, v.ExpectedType.String(), v.String())
|
||||
return Message(
|
||||
`Expected value of type "%s", found %s.`,
|
||||
v.ExpectedType.String(),
|
||||
v.String(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Generated
Vendored
-1
@@ -2,7 +2,6 @@ package rules
|
||||
|
||||
import (
|
||||
"github.com/vektah/gqlparser/v2/ast"
|
||||
|
||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
||||
)
|
||||
|
||||
Generated
Vendored
+10
-3
@@ -2,7 +2,6 @@ package rules
|
||||
|
||||
import (
|
||||
"github.com/vektah/gqlparser/v2/ast"
|
||||
|
||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
||||
)
|
||||
@@ -11,7 +10,9 @@ var VariablesInAllowedPositionRule = Rule{
|
||||
Name: "VariablesInAllowedPosition",
|
||||
RuleFunc: func(observers *Events, addError AddErrFunc) {
|
||||
observers.OnValue(func(walker *Walker, value *ast.Value) {
|
||||
if value.Kind != ast.Variable || value.ExpectedType == nil || value.VariableDefinition == nil || walker.CurrentOperation == nil {
|
||||
if value.Kind != ast.Variable || value.ExpectedType == nil ||
|
||||
value.VariableDefinition == nil ||
|
||||
walker.CurrentOperation == nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -19,12 +20,18 @@ var VariablesInAllowedPositionRule = Rule{
|
||||
|
||||
// todo: move me into walk
|
||||
// If there is a default non nullable types can be null
|
||||
if value.VariableDefinition.DefaultValue != nil && value.VariableDefinition.DefaultValue.Kind != ast.NullValue {
|
||||
if value.VariableDefinition.DefaultValue != nil &&
|
||||
value.VariableDefinition.DefaultValue.Kind != ast.NullValue {
|
||||
if value.ExpectedType.NonNull {
|
||||
tmp.NonNull = false
|
||||
}
|
||||
}
|
||||
|
||||
// If the expected type has a default, the given variable can be null
|
||||
if value.ExpectedTypeHasDefault {
|
||||
tmp.NonNull = false
|
||||
}
|
||||
|
||||
if !value.VariableDefinition.Type.IsCompatible(&tmp) {
|
||||
addError(
|
||||
Message(
|
||||
|
||||
+177
-53
@@ -1,6 +1,7 @@
|
||||
package validator
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -48,7 +49,13 @@ func ValidateSchemaDocument(sd *SchemaDocument) (*Schema, error) {
|
||||
}
|
||||
|
||||
if def.Kind != ext.Kind {
|
||||
return nil, gqlerror.ErrorPosf(ext.Position, "Cannot extend type %s because the base type is a %s, not %s.", ext.Name, def.Kind, ext.Kind)
|
||||
return nil, gqlerror.ErrorPosf(
|
||||
ext.Position,
|
||||
"Cannot extend type %s because the base type is a %s, not %s.",
|
||||
ext.Name,
|
||||
def.Kind,
|
||||
ext.Kind,
|
||||
)
|
||||
}
|
||||
|
||||
def.Directives = append(def.Directives, ext.Directives...)
|
||||
@@ -95,14 +102,21 @@ func ValidateSchemaDocument(sd *SchemaDocument) (*Schema, error) {
|
||||
// version of gqlparser, in which case they're in trouble
|
||||
// anyway.
|
||||
default:
|
||||
return nil, gqlerror.ErrorPosf(dir.Position, "Cannot redeclare directive %s.", dir.Name)
|
||||
return nil, gqlerror.ErrorPosf(
|
||||
dir.Position,
|
||||
"Cannot redeclare directive %s.",
|
||||
dir.Name,
|
||||
)
|
||||
}
|
||||
}
|
||||
schema.Directives[dir.Name] = sd.Directives[i]
|
||||
}
|
||||
|
||||
if len(sd.Schema) > 1 {
|
||||
return nil, gqlerror.ErrorPosf(sd.Schema[1].Position, "Cannot have multiple schema entry points, consider schema extensions instead.")
|
||||
return nil, gqlerror.ErrorPosf(
|
||||
sd.Schema[1].Position,
|
||||
"Cannot have multiple schema entry points, consider schema extensions instead.",
|
||||
)
|
||||
}
|
||||
|
||||
if len(sd.Schema) == 1 {
|
||||
@@ -110,7 +124,12 @@ func ValidateSchemaDocument(sd *SchemaDocument) (*Schema, error) {
|
||||
for _, entrypoint := range sd.Schema[0].OperationTypes {
|
||||
def := schema.Types[entrypoint.Type]
|
||||
if def == nil {
|
||||
return nil, gqlerror.ErrorPosf(entrypoint.Position, "Schema root %s refers to a type %s that does not exist.", entrypoint.Operation, entrypoint.Type)
|
||||
return nil, gqlerror.ErrorPosf(
|
||||
entrypoint.Position,
|
||||
"Schema root %s refers to a type %s that does not exist.",
|
||||
entrypoint.Operation,
|
||||
entrypoint.Type,
|
||||
)
|
||||
}
|
||||
switch entrypoint.Operation {
|
||||
case Query:
|
||||
@@ -121,7 +140,12 @@ func ValidateSchemaDocument(sd *SchemaDocument) (*Schema, error) {
|
||||
schema.Subscription = def
|
||||
}
|
||||
}
|
||||
if err := validateDirectives(&schema, sd.Schema[0].Directives, LocationSchema, nil); err != nil {
|
||||
if err := validateDirectives(
|
||||
&schema,
|
||||
sd.Schema[0].Directives,
|
||||
LocationSchema,
|
||||
nil,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
schema.SchemaDirectives = append(schema.SchemaDirectives, sd.Schema[0].Directives...)
|
||||
@@ -131,7 +155,12 @@ func ValidateSchemaDocument(sd *SchemaDocument) (*Schema, error) {
|
||||
for _, entrypoint := range ext.OperationTypes {
|
||||
def := schema.Types[entrypoint.Type]
|
||||
if def == nil {
|
||||
return nil, gqlerror.ErrorPosf(entrypoint.Position, "Schema root %s refers to a type %s that does not exist.", entrypoint.Operation, entrypoint.Type)
|
||||
return nil, gqlerror.ErrorPosf(
|
||||
entrypoint.Position,
|
||||
"Schema root %s refers to a type %s that does not exist.",
|
||||
entrypoint.Operation,
|
||||
entrypoint.Type,
|
||||
)
|
||||
}
|
||||
switch entrypoint.Operation {
|
||||
case Query:
|
||||
@@ -259,7 +288,13 @@ func validateDefinition(schema *Schema, def *Definition) *gqlerror.Error {
|
||||
return gqlerror.ErrorPosf(def.Position, "Undefined type %s.", strconv.Quote(typ))
|
||||
}
|
||||
if !isValidKind(typDef.Kind, Object) {
|
||||
return gqlerror.ErrorPosf(def.Position, "%s type %s must be %s.", def.Kind, strconv.Quote(typ), kindList(Object))
|
||||
return gqlerror.ErrorPosf(
|
||||
def.Position,
|
||||
"%s type %s must be %s.",
|
||||
def.Kind,
|
||||
strconv.Quote(typ),
|
||||
kindList(Object),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,37 +307,75 @@ func validateDefinition(schema *Schema, def *Definition) *gqlerror.Error {
|
||||
switch def.Kind {
|
||||
case Object, Interface:
|
||||
if len(def.Fields) == 0 {
|
||||
return gqlerror.ErrorPosf(def.Position, "%s %s: must define one or more fields.", def.Kind, def.Name)
|
||||
return gqlerror.ErrorPosf(
|
||||
def.Position,
|
||||
"%s %s: must define one or more fields.",
|
||||
def.Kind,
|
||||
def.Name,
|
||||
)
|
||||
}
|
||||
for _, field := range def.Fields {
|
||||
if typ, ok := schema.Types[field.Type.Name()]; ok {
|
||||
if !isValidKind(typ.Kind, Scalar, Object, Interface, Union, Enum) {
|
||||
return gqlerror.ErrorPosf(field.Position, "%s %s: field must be one of %s.", def.Kind, def.Name, kindList(Scalar, Object, Interface, Union, Enum))
|
||||
return gqlerror.ErrorPosf(
|
||||
field.Position,
|
||||
"%s %s: field must be one of %s.",
|
||||
def.Kind,
|
||||
def.Name,
|
||||
kindList(Scalar, Object, Interface, Union, Enum),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
case Enum:
|
||||
if len(def.EnumValues) == 0 {
|
||||
return gqlerror.ErrorPosf(def.Position, "%s %s: must define one or more unique enum values.", def.Kind, def.Name)
|
||||
return gqlerror.ErrorPosf(
|
||||
def.Position,
|
||||
"%s %s: must define one or more unique enum values.",
|
||||
def.Kind,
|
||||
def.Name,
|
||||
)
|
||||
}
|
||||
for _, value := range def.EnumValues {
|
||||
for _, nonEnum := range [3]string{"true", "false", "null"} {
|
||||
if value.Name == nonEnum {
|
||||
return gqlerror.ErrorPosf(def.Position, "%s %s: non-enum value %s.", def.Kind, def.Name, value.Name)
|
||||
return gqlerror.ErrorPosf(
|
||||
def.Position,
|
||||
"%s %s: non-enum value %s.",
|
||||
def.Kind,
|
||||
def.Name,
|
||||
value.Name,
|
||||
)
|
||||
}
|
||||
}
|
||||
if err := validateDirectives(schema, value.Directives, LocationEnumValue, nil); err != nil {
|
||||
if err := validateDirectives(
|
||||
schema,
|
||||
value.Directives,
|
||||
LocationEnumValue,
|
||||
nil,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case InputObject:
|
||||
if len(def.Fields) == 0 {
|
||||
return gqlerror.ErrorPosf(def.Position, "%s %s: must define one or more input fields.", def.Kind, def.Name)
|
||||
return gqlerror.ErrorPosf(
|
||||
def.Position,
|
||||
"%s %s: must define one or more input fields.",
|
||||
def.Kind,
|
||||
def.Name,
|
||||
)
|
||||
}
|
||||
for _, field := range def.Fields {
|
||||
if typ, ok := schema.Types[field.Type.Name()]; ok {
|
||||
if !isValidKind(typ.Kind, Scalar, Enum, InputObject) {
|
||||
return gqlerror.ErrorPosf(field.Position, "%s %s: field must be one of %s.", typ.Kind, field.Name, kindList(Scalar, Enum, InputObject))
|
||||
return gqlerror.ErrorPosf(
|
||||
field.Position,
|
||||
"%s %s: field must be one of %s.",
|
||||
typ.Kind,
|
||||
field.Name,
|
||||
kindList(Scalar, Enum, InputObject),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -311,7 +384,12 @@ func validateDefinition(schema *Schema, def *Definition) *gqlerror.Error {
|
||||
for idx, field1 := range def.Fields {
|
||||
for _, field2 := range def.Fields[idx+1:] {
|
||||
if field1.Name == field2.Name {
|
||||
return gqlerror.ErrorPosf(field2.Position, "Field %s.%s can only be defined once.", def.Name, field2.Name)
|
||||
return gqlerror.ErrorPosf(
|
||||
field2.Position,
|
||||
"Field %s.%s can only be defined once.",
|
||||
def.Name,
|
||||
field2.Name,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -334,7 +412,11 @@ func validateTypeRef(schema *Schema, typ *Type) *gqlerror.Error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateArgs(schema *Schema, args ArgumentDefinitionList, currentDirective *DirectiveDefinition) *gqlerror.Error {
|
||||
func validateArgs(
|
||||
schema *Schema,
|
||||
args ArgumentDefinitionList,
|
||||
currentDirective *DirectiveDefinition,
|
||||
) *gqlerror.Error {
|
||||
for _, arg := range args {
|
||||
if err := validateName(arg.Position, arg.Name); err != nil {
|
||||
// now, GraphQL spec doesn't have reserved argument name
|
||||
@@ -353,45 +435,71 @@ func validateArgs(schema *Schema, args ArgumentDefinitionList, currentDirective
|
||||
def.Kind,
|
||||
)
|
||||
}
|
||||
if err := validateDirectives(schema, arg.Directives, LocationArgumentDefinition, currentDirective); err != nil {
|
||||
if err := validateDirectives(
|
||||
schema,
|
||||
arg.Directives,
|
||||
LocationArgumentDefinition,
|
||||
currentDirective,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateDirectives(schema *Schema, dirs DirectiveList, location DirectiveLocation, currentDirective *DirectiveDefinition) *gqlerror.Error {
|
||||
func validateDirectives(
|
||||
schema *Schema,
|
||||
dirs DirectiveList,
|
||||
location DirectiveLocation,
|
||||
currentDirective *DirectiveDefinition,
|
||||
) *gqlerror.Error {
|
||||
for _, dir := range dirs {
|
||||
if err := validateName(dir.Position, dir.Name); err != nil {
|
||||
// now, GraphQL spec doesn't have reserved directive name
|
||||
return err
|
||||
}
|
||||
if currentDirective != nil && dir.Name == currentDirective.Name {
|
||||
return gqlerror.ErrorPosf(dir.Position, "Directive %s cannot refer to itself.", currentDirective.Name)
|
||||
return gqlerror.ErrorPosf(
|
||||
dir.Position,
|
||||
"Directive %s cannot refer to itself.",
|
||||
currentDirective.Name,
|
||||
)
|
||||
}
|
||||
dirDefinition := schema.Directives[dir.Name]
|
||||
if dirDefinition == nil {
|
||||
return gqlerror.ErrorPosf(dir.Position, "Undefined directive %s.", dir.Name)
|
||||
}
|
||||
validKind := false
|
||||
for _, dirLocation := range dirDefinition.Locations {
|
||||
if dirLocation == location {
|
||||
validKind = true
|
||||
break
|
||||
}
|
||||
}
|
||||
validKind := slices.Contains(dirDefinition.Locations, location)
|
||||
if !validKind {
|
||||
return gqlerror.ErrorPosf(dir.Position, "Directive %s is not applicable on %s.", dir.Name, location)
|
||||
return gqlerror.ErrorPosf(
|
||||
dir.Position,
|
||||
"Directive %s is not applicable on %s.",
|
||||
dir.Name,
|
||||
location,
|
||||
)
|
||||
}
|
||||
for _, arg := range dir.Arguments {
|
||||
if dirDefinition.Arguments.ForName(arg.Name) == nil {
|
||||
return gqlerror.ErrorPosf(arg.Position, "Undefined argument %s for directive %s.", arg.Name, dir.Name)
|
||||
return gqlerror.ErrorPosf(
|
||||
arg.Position,
|
||||
"Undefined argument %s for directive %s.",
|
||||
arg.Name,
|
||||
dir.Name,
|
||||
)
|
||||
}
|
||||
}
|
||||
for _, schemaArg := range dirDefinition.Arguments {
|
||||
if schemaArg.Type.NonNull && schemaArg.DefaultValue == nil {
|
||||
if arg := dir.Arguments.ForName(schemaArg.Name); arg == nil || arg.Value.Kind == NullValue {
|
||||
return gqlerror.ErrorPosf(dir.Position, "Argument %s for directive %s cannot be null.", schemaArg.Name, dir.Name)
|
||||
if arg := dir.Arguments.ForName(
|
||||
schemaArg.Name,
|
||||
); arg == nil ||
|
||||
arg.Value.Kind == NullValue {
|
||||
return gqlerror.ErrorPosf(
|
||||
dir.Position,
|
||||
"Argument %s for directive %s cannot be null.",
|
||||
schemaArg.Name,
|
||||
dir.Name,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -408,7 +516,12 @@ func validateImplements(schema *Schema, def *Definition, intfName string) *gqler
|
||||
return gqlerror.ErrorPosf(def.Position, "Undefined type %s.", strconv.Quote(intfName))
|
||||
}
|
||||
if intf.Kind != Interface {
|
||||
return gqlerror.ErrorPosf(def.Position, "%s is a non interface type %s.", strconv.Quote(intfName), intf.Kind)
|
||||
return gqlerror.ErrorPosf(
|
||||
def.Position,
|
||||
"%s is a non interface type %s.",
|
||||
strconv.Quote(intfName),
|
||||
intf.Kind,
|
||||
)
|
||||
}
|
||||
for _, requiredField := range intf.Fields {
|
||||
foundField := def.Fields.ForName(requiredField.Name)
|
||||
@@ -429,24 +542,37 @@ func validateImplements(schema *Schema, def *Definition, intfName string) *gqler
|
||||
for _, requiredArg := range requiredField.Arguments {
|
||||
foundArg := foundField.Arguments.ForName(requiredArg.Name)
|
||||
if foundArg == nil {
|
||||
return gqlerror.ErrorPosf(foundField.Position,
|
||||
return gqlerror.ErrorPosf(
|
||||
foundField.Position,
|
||||
`For %s to implement %s the field %s must have the same arguments but it is missing %s.`,
|
||||
def.Name, intf.Name, requiredField.Name, requiredArg.Name,
|
||||
def.Name,
|
||||
intf.Name,
|
||||
requiredField.Name,
|
||||
requiredArg.Name,
|
||||
)
|
||||
}
|
||||
|
||||
if !requiredArg.Type.IsCompatible(foundArg.Type) {
|
||||
return gqlerror.ErrorPosf(foundArg.Position,
|
||||
return gqlerror.ErrorPosf(
|
||||
foundArg.Position,
|
||||
`For %s to implement %s the field %s must have the same arguments but %s has the wrong type.`,
|
||||
def.Name, intf.Name, requiredField.Name, requiredArg.Name,
|
||||
def.Name,
|
||||
intf.Name,
|
||||
requiredField.Name,
|
||||
requiredArg.Name,
|
||||
)
|
||||
}
|
||||
}
|
||||
for _, foundArgs := range foundField.Arguments {
|
||||
if requiredField.Arguments.ForName(foundArgs.Name) == nil && foundArgs.Type.NonNull && foundArgs.DefaultValue == nil {
|
||||
return gqlerror.ErrorPosf(foundArgs.Position,
|
||||
if requiredField.Arguments.ForName(foundArgs.Name) == nil && foundArgs.Type.NonNull &&
|
||||
foundArgs.DefaultValue == nil {
|
||||
return gqlerror.ErrorPosf(
|
||||
foundArgs.Position,
|
||||
`For %s to implement %s any additional arguments on %s must be optional or have a default value but %s is required.`,
|
||||
def.Name, intf.Name, foundField.Name, foundArgs.Name,
|
||||
def.Name,
|
||||
intf.Name,
|
||||
foundField.Name,
|
||||
foundArgs.Name,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -456,7 +582,11 @@ func validateImplements(schema *Schema, def *Definition, intfName string) *gqler
|
||||
|
||||
// validateTypeImplementsAncestors
|
||||
// https://github.com/graphql/graphql-js/blob/47bd8c8897c72d3efc17ecb1599a95cee6bac5e8/src/type/validate.ts#L428
|
||||
func validateTypeImplementsAncestors(schema *Schema, def *Definition, intfName string) *gqlerror.Error {
|
||||
func validateTypeImplementsAncestors(
|
||||
schema *Schema,
|
||||
def *Definition,
|
||||
intfName string,
|
||||
) *gqlerror.Error {
|
||||
intf := schema.Types[intfName]
|
||||
if intf == nil {
|
||||
return gqlerror.ErrorPosf(def.Position, "Undefined type %s.", strconv.Quote(intfName))
|
||||
@@ -479,15 +609,10 @@ func validateTypeImplementsAncestors(schema *Schema, def *Definition, intfName s
|
||||
}
|
||||
|
||||
func containsString(slice []string, want string) bool {
|
||||
for _, str := range slice {
|
||||
if want == str {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
return slices.Contains(slice, want)
|
||||
}
|
||||
|
||||
func isCovariant(schema *Schema, required *Type, actual *Type) bool {
|
||||
func isCovariant(schema *Schema, required, actual *Type) bool {
|
||||
if required.NonNull && !actual.NonNull {
|
||||
return false
|
||||
}
|
||||
@@ -513,18 +638,17 @@ func isCovariant(schema *Schema, required *Type, actual *Type) bool {
|
||||
|
||||
func validateName(pos *Position, name string) *gqlerror.Error {
|
||||
if strings.HasPrefix(name, "__") {
|
||||
return gqlerror.ErrorPosf(pos, `Name "%s" must not begin with "__", which is reserved by GraphQL introspection.`, name)
|
||||
return gqlerror.ErrorPosf(
|
||||
pos,
|
||||
`Name "%s" must not begin with "__", which is reserved by GraphQL introspection.`,
|
||||
name,
|
||||
)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func isValidKind(kind DefinitionKind, valid ...DefinitionKind) bool {
|
||||
for _, k := range valid {
|
||||
if kind == k {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
return slices.Contains(valid, kind)
|
||||
}
|
||||
|
||||
func kindList(kinds ...DefinitionKind) string {
|
||||
|
||||
+12
-7
@@ -2,6 +2,7 @@ package validator
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
//nolint:staticcheck // bad, yeah
|
||||
. "github.com/vektah/gqlparser/v2/ast"
|
||||
"github.com/vektah/gqlparser/v2/gqlerror"
|
||||
@@ -24,7 +25,7 @@ var (
|
||||
OrList = core.OrList
|
||||
)
|
||||
|
||||
// Walk is an alias for core.Walk
|
||||
// Walk is an alias for core.Walk.
|
||||
func Walk(schema *Schema, document *QueryDocument, observers *Events) {
|
||||
core.Walk(schema, document, observers)
|
||||
}
|
||||
@@ -49,9 +50,9 @@ func AddRule(name string, ruleFunc RuleFunc) {
|
||||
|
||||
// RemoveRule removes an existing rule from the rule set
|
||||
// if one of the same name exists.
|
||||
// The rule set is global, so it is not safe for concurrent changes
|
||||
// The rule set is global, so it is not safe for concurrent changes.
|
||||
func RemoveRule(name string) {
|
||||
var result []Rule // nolint:prealloc // using initialized with len(rules) produces a race condition
|
||||
var result []Rule //nolint:prealloc // using initialized with len(rules) produces a race condition
|
||||
for _, r := range specifiedRules {
|
||||
if r.Name == name {
|
||||
continue
|
||||
@@ -64,10 +65,10 @@ func RemoveRule(name string) {
|
||||
// ReplaceRule replaces an existing rule from the rule set
|
||||
// if one of the same name exists.
|
||||
// If no match is found, it will add a new rule to the rule set.
|
||||
// The rule set is global, so it is not safe for concurrent changes
|
||||
// The rule set is global, so it is not safe for concurrent changes.
|
||||
func ReplaceRule(name string, ruleFunc RuleFunc) {
|
||||
var found bool
|
||||
var result []Rule // nolint:prealloc // using initialized with len(rules) produces a race condition
|
||||
var result []Rule //nolint:prealloc // using initialized with len(rules) produces a race condition
|
||||
for _, r := range specifiedRules {
|
||||
if r.Name == name {
|
||||
found = true
|
||||
@@ -117,7 +118,11 @@ func Validate(schema *Schema, doc *QueryDocument, rules ...Rule) gqlerror.List {
|
||||
return errs
|
||||
}
|
||||
|
||||
func ValidateWithRules(schema *Schema, doc *QueryDocument, rules *validatorrules.Rules) gqlerror.List {
|
||||
func ValidateWithRules(
|
||||
schema *Schema,
|
||||
doc *QueryDocument,
|
||||
rules *validatorrules.Rules,
|
||||
) gqlerror.List {
|
||||
if rules == nil {
|
||||
rules = validatorrules.NewDefaultRules()
|
||||
}
|
||||
@@ -134,7 +139,7 @@ func ValidateWithRules(schema *Schema, doc *QueryDocument, rules *validatorrules
|
||||
}
|
||||
observers := &core.Events{}
|
||||
|
||||
var currentRules []Rule // nolint:prealloc // would require extra local refs for len
|
||||
var currentRules []Rule //nolint:prealloc // would require extra local refs for len
|
||||
for name, ruleFunc := range rules.GetInner() {
|
||||
currentRules = append(currentRules, Rule{Name: name, RuleFunc: ruleFunc})
|
||||
// ensure deterministic order evaluation
|
||||
|
||||
+37
-11
@@ -2,6 +2,7 @@ package validator
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
@@ -12,11 +13,15 @@ import (
|
||||
)
|
||||
|
||||
//nolint:staticcheck // We do not care about capitalized error strings
|
||||
var ErrUnexpectedType = fmt.Errorf("Unexpected Type")
|
||||
var ErrUnexpectedType = errors.New("Unexpected Type")
|
||||
|
||||
// VariableValues coerces and validates variable values
|
||||
func VariableValues(schema *ast.Schema, op *ast.OperationDefinition, variables map[string]interface{}) (map[string]interface{}, error) {
|
||||
coercedVars := map[string]interface{}{}
|
||||
// VariableValues coerces and validates variable values.
|
||||
func VariableValues(
|
||||
schema *ast.Schema,
|
||||
op *ast.OperationDefinition,
|
||||
variables map[string]any,
|
||||
) (map[string]any, error) {
|
||||
coercedVars := map[string]any{}
|
||||
|
||||
validator := varValidator{
|
||||
path: ast.Path{ast.PathName("variable")},
|
||||
@@ -60,13 +65,23 @@ func VariableValues(schema *ast.Schema, op *ast.OperationDefinition, variables m
|
||||
case "Int":
|
||||
n, err := jsonNumber.Int64()
|
||||
if err != nil {
|
||||
return nil, gqlerror.ErrorPathf(validator.path, "cannot use value %d as %s", n, v.Type.NamedType)
|
||||
return nil, gqlerror.ErrorPathf(
|
||||
validator.path,
|
||||
"cannot use value %d as %s",
|
||||
n,
|
||||
v.Type.NamedType,
|
||||
)
|
||||
}
|
||||
rv = reflect.ValueOf(n)
|
||||
case "Float":
|
||||
f, err := jsonNumber.Float64()
|
||||
if err != nil {
|
||||
return nil, gqlerror.ErrorPathf(validator.path, "cannot use value %f as %s", f, v.Type.NamedType)
|
||||
return nil, gqlerror.ErrorPathf(
|
||||
validator.path,
|
||||
"cannot use value %f as %s",
|
||||
f,
|
||||
v.Type.NamedType,
|
||||
)
|
||||
}
|
||||
rv = reflect.ValueOf(f)
|
||||
}
|
||||
@@ -93,7 +108,10 @@ type varValidator struct {
|
||||
schema *ast.Schema
|
||||
}
|
||||
|
||||
func (v *varValidator) validateVarType(typ *ast.Type, val reflect.Value) (reflect.Value, *gqlerror.Error) {
|
||||
func (v *varValidator) validateVarType(
|
||||
typ *ast.Type,
|
||||
val reflect.Value,
|
||||
) (reflect.Value, *gqlerror.Error) {
|
||||
currentPath := v.path
|
||||
resetPath := func() {
|
||||
v.path = currentPath
|
||||
@@ -137,7 +155,8 @@ func (v *varValidator) validateVarType(typ *ast.Type, val reflect.Value) (reflec
|
||||
switch def.Kind {
|
||||
case ast.Enum:
|
||||
kind := val.Type().Kind()
|
||||
if kind != reflect.Int && kind != reflect.Int32 && kind != reflect.Int64 && kind != reflect.String {
|
||||
if kind != reflect.Int && kind != reflect.Int32 && kind != reflect.Int64 &&
|
||||
kind != reflect.String {
|
||||
return val, gqlerror.ErrorPathf(v.path, "enums must be ints or strings")
|
||||
}
|
||||
isValidEnum := false
|
||||
@@ -154,11 +173,17 @@ func (v *varValidator) validateVarType(typ *ast.Type, val reflect.Value) (reflec
|
||||
kind := val.Type().Kind()
|
||||
switch typ.NamedType {
|
||||
case "Int":
|
||||
if kind == reflect.Int || kind == reflect.Int32 || kind == reflect.Int64 || kind == reflect.Float32 || kind == reflect.Float64 || IsValidIntString(val, kind) {
|
||||
if kind == reflect.Int || kind == reflect.Int32 || kind == reflect.Int64 ||
|
||||
kind == reflect.Float32 ||
|
||||
kind == reflect.Float64 ||
|
||||
IsValidIntString(val, kind) {
|
||||
return val, nil
|
||||
}
|
||||
case "Float":
|
||||
if kind == reflect.Float32 || kind == reflect.Float64 || kind == reflect.Int || kind == reflect.Int32 || kind == reflect.Int64 || IsValidFloatString(val, kind) {
|
||||
if kind == reflect.Float32 || kind == reflect.Float64 || kind == reflect.Int ||
|
||||
kind == reflect.Int32 ||
|
||||
kind == reflect.Int64 ||
|
||||
IsValidFloatString(val, kind) {
|
||||
return val, nil
|
||||
}
|
||||
case "String":
|
||||
@@ -172,7 +197,8 @@ func (v *varValidator) validateVarType(typ *ast.Type, val reflect.Value) (reflec
|
||||
}
|
||||
|
||||
case "ID":
|
||||
if kind == reflect.Int || kind == reflect.Int32 || kind == reflect.Int64 || kind == reflect.String {
|
||||
if kind == reflect.Int || kind == reflect.Int32 || kind == reflect.Int64 ||
|
||||
kind == reflect.String {
|
||||
return val, nil
|
||||
}
|
||||
default:
|
||||
|
||||
Vendored
+1
-1
@@ -1132,7 +1132,7 @@ github.com/transparency-dev/merkle/rfc6962
|
||||
## explicit; go 1.12
|
||||
github.com/valyala/fastjson
|
||||
github.com/valyala/fastjson/fastfloat
|
||||
# github.com/vektah/gqlparser/v2 v2.5.30
|
||||
# github.com/vektah/gqlparser/v2 v2.5.32
|
||||
## explicit; go 1.22
|
||||
github.com/vektah/gqlparser/v2/ast
|
||||
github.com/vektah/gqlparser/v2/gqlerror
|
||||
|
||||
Reference in New Issue
Block a user