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/formats v0.1.1 // indirect
|
||||||
github.com/transparency-dev/merkle v0.0.2 // indirect
|
github.com/transparency-dev/merkle v0.0.2 // indirect
|
||||||
github.com/valyala/fastjson v1.6.7 // 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/x448/float16 v0.8.4 // indirect
|
||||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // 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/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 h1:Cd46rkGXI3Td4yrVNwU8ripbxFaQbmesqhjBUUYAJSw=
|
||||||
github.com/vbatts/tar-split v0.12.3/go.mod h1:sQOc6OlqGCr7HkGx/IDBeKiTIvqhmj8KffNhEXG4Nq0=
|
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.32 h1:k9QPJd4sEDTL+qB4ncPLflqTJ3MmjB9SrVzJrawpFSc=
|
||||||
github.com/vektah/gqlparser/v2 v2.5.30/go.mod h1:D1/VCZtV3LPnQrcPBeR/q5jkSQIPti0uYCP/RI0gIeo=
|
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 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
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=
|
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
|
||||||
|
|||||||
+7
-3
@@ -1,11 +1,15 @@
|
|||||||
package ast
|
package ast
|
||||||
|
|
||||||
func arg2map(defs ArgumentDefinitionList, args ArgumentList, vars map[string]interface{}) map[string]interface{} {
|
func arg2map(
|
||||||
result := map[string]interface{}{}
|
defs ArgumentDefinitionList,
|
||||||
|
args ArgumentList,
|
||||||
|
vars map[string]any,
|
||||||
|
) map[string]any {
|
||||||
|
result := map[string]any{}
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
for _, argDef := range defs {
|
for _, argDef := range defs {
|
||||||
var val interface{}
|
var val any
|
||||||
var hasValue bool
|
var hasValue bool
|
||||||
|
|
||||||
if argValue := args.ForName(argDef.Name); argValue != nil {
|
if argValue := args.ForName(argDef.Name); argValue != nil {
|
||||||
|
|||||||
+3
-6
@@ -1,5 +1,7 @@
|
|||||||
package ast
|
package ast
|
||||||
|
|
||||||
|
import "slices"
|
||||||
|
|
||||||
type DefinitionKind string
|
type DefinitionKind string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -54,12 +56,7 @@ func (d *Definition) IsInputType() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *Definition) OneOf(types ...string) bool {
|
func (d *Definition) OneOf(types ...string) bool {
|
||||||
for _, t := range types {
|
return slices.Contains(types, d.Name)
|
||||||
if d.Name == t {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type FieldDefinition struct {
|
type FieldDefinition struct {
|
||||||
|
|||||||
+6
-3
@@ -3,7 +3,7 @@ package ast
|
|||||||
type DirectiveLocation string
|
type DirectiveLocation string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Executable
|
// Executable.
|
||||||
LocationQuery DirectiveLocation = `QUERY`
|
LocationQuery DirectiveLocation = `QUERY`
|
||||||
LocationMutation DirectiveLocation = `MUTATION`
|
LocationMutation DirectiveLocation = `MUTATION`
|
||||||
LocationSubscription DirectiveLocation = `SUBSCRIPTION`
|
LocationSubscription DirectiveLocation = `SUBSCRIPTION`
|
||||||
@@ -12,7 +12,7 @@ const (
|
|||||||
LocationFragmentSpread DirectiveLocation = `FRAGMENT_SPREAD`
|
LocationFragmentSpread DirectiveLocation = `FRAGMENT_SPREAD`
|
||||||
LocationInlineFragment DirectiveLocation = `INLINE_FRAGMENT`
|
LocationInlineFragment DirectiveLocation = `INLINE_FRAGMENT`
|
||||||
|
|
||||||
// Type System
|
// Type System.
|
||||||
LocationSchema DirectiveLocation = `SCHEMA`
|
LocationSchema DirectiveLocation = `SCHEMA`
|
||||||
LocationScalar DirectiveLocation = `SCALAR`
|
LocationScalar DirectiveLocation = `SCALAR`
|
||||||
LocationObject DirectiveLocation = `OBJECT`
|
LocationObject DirectiveLocation = `OBJECT`
|
||||||
@@ -38,6 +38,9 @@ type Directive struct {
|
|||||||
Location DirectiveLocation
|
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)
|
return arg2map(d.Definition.Arguments, d.Arguments, vars)
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-3
@@ -42,7 +42,7 @@ type Schema struct {
|
|||||||
Comment *CommentGroup
|
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) {
|
func (s *Schema) AddTypes(defs ...*Definition) {
|
||||||
if s.Types == nil {
|
if s.Types == nil {
|
||||||
s.Types = make(map[string]*Definition)
|
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)
|
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 {
|
func (s *Schema) GetPossibleTypes(def *Definition) []*Definition {
|
||||||
return s.PossibleTypes[def.Name]
|
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)
|
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 {
|
func (s *Schema) GetImplements(def *Definition) []*Definition {
|
||||||
return s.Implements[def.Name]
|
return s.Implements[def.Name]
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-6
@@ -8,8 +8,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Dump turns ast into a stable string format for assertions in tests
|
// Dump turns ast into a stable string format for assertions in tests.
|
||||||
func Dump(i interface{}) string {
|
func Dump(i any) string {
|
||||||
v := reflect.ValueOf(i)
|
v := reflect.ValueOf(i)
|
||||||
|
|
||||||
d := dumper{Buffer: &bytes.Buffer{}}
|
d := dumper{Buffer: &bytes.Buffer{}}
|
||||||
@@ -126,7 +126,6 @@ func isZero(v reflect.Value) bool {
|
|||||||
return v.IsNil()
|
return v.IsNil()
|
||||||
case reflect.Func, reflect.Map:
|
case reflect.Func, reflect.Map:
|
||||||
return v.IsNil()
|
return v.IsNil()
|
||||||
|
|
||||||
case reflect.Array, reflect.Slice:
|
case reflect.Array, reflect.Slice:
|
||||||
if v.IsNil() {
|
if v.IsNil() {
|
||||||
return true
|
return true
|
||||||
@@ -144,10 +143,13 @@ func isZero(v reflect.Value) bool {
|
|||||||
return z
|
return z
|
||||||
case reflect.String:
|
case reflect.String:
|
||||||
return v.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) {
|
func (d *dumper) dumpPtr(v reflect.Value) {
|
||||||
|
|||||||
+2
-2
@@ -27,7 +27,7 @@ func (path Path) String() string {
|
|||||||
for i, v := range path {
|
for i, v := range path {
|
||||||
switch v := v.(type) {
|
switch v := v.(type) {
|
||||||
case PathIndex:
|
case PathIndex:
|
||||||
str.WriteString(fmt.Sprintf("[%d]", v))
|
fmt.Fprintf(&str, "[%d]", v)
|
||||||
case PathName:
|
case PathName:
|
||||||
if i != 0 {
|
if i != 0 {
|
||||||
str.WriteByte('.')
|
str.WriteByte('.')
|
||||||
@@ -41,7 +41,7 @@ func (path Path) String() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (path *Path) UnmarshalJSON(b []byte) error {
|
func (path *Path) UnmarshalJSON(b []byte) error {
|
||||||
var vs []interface{}
|
var vs []any
|
||||||
err := json.Unmarshal(b, &vs)
|
err := json.Unmarshal(b, &vs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
+4
-1
@@ -36,6 +36,9 @@ type Argument struct {
|
|||||||
Comment *CommentGroup
|
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)
|
return arg2map(f.Definition.Arguments, f.Arguments, vars)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
package ast
|
package ast
|
||||||
|
|
||||||
// Source covers a single *.graphql file
|
// Source covers a single *.graphql file.
|
||||||
type Source struct {
|
type Source struct {
|
||||||
// Name is the filename of the source
|
// Name is the filename of the source
|
||||||
Name string
|
Name string
|
||||||
|
|||||||
+7
-6
@@ -29,9 +29,10 @@ type Value struct {
|
|||||||
Comment *CommentGroup
|
Comment *CommentGroup
|
||||||
|
|
||||||
// Require validation
|
// Require validation
|
||||||
Definition *Definition
|
Definition *Definition
|
||||||
VariableDefinition *VariableDefinition
|
VariableDefinition *VariableDefinition
|
||||||
ExpectedType *Type
|
ExpectedType *Type
|
||||||
|
ExpectedTypeHasDefault bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChildValue struct {
|
type ChildValue struct {
|
||||||
@@ -41,7 +42,7 @@ type ChildValue struct {
|
|||||||
Comment *CommentGroup
|
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 {
|
if v == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@@ -65,7 +66,7 @@ func (v *Value) Value(vars map[string]interface{}) (interface{}, error) {
|
|||||||
case NullValue:
|
case NullValue:
|
||||||
return nil, nil
|
return nil, nil
|
||||||
case ListValue:
|
case ListValue:
|
||||||
var val []interface{}
|
var val []any
|
||||||
for _, elem := range v.Children {
|
for _, elem := range v.Children {
|
||||||
elemVal, err := elem.Value.Value(vars)
|
elemVal, err := elem.Value.Value(vars)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -75,7 +76,7 @@ func (v *Value) Value(vars map[string]interface{}) (interface{}, error) {
|
|||||||
}
|
}
|
||||||
return val, nil
|
return val, nil
|
||||||
case ObjectValue:
|
case ObjectValue:
|
||||||
val := map[string]interface{}{}
|
val := map[string]any{}
|
||||||
for _, elem := range v.Children {
|
for _, elem := range v.Children {
|
||||||
elemVal, err := elem.Value.Value(vars)
|
elemVal, err := elem.Value.Value(vars)
|
||||||
if err != nil {
|
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
|
// Error is the standard graphql error type described in https://spec.graphql.org/draft/#sec-Errors
|
||||||
type Error struct {
|
type Error struct {
|
||||||
Err error `json:"-"`
|
Err error `json:"-"`
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
Path ast.Path `json:"path,omitempty"`
|
Path ast.Path `json:"path,omitempty"`
|
||||||
Locations []Location `json:"locations,omitempty"`
|
Locations []Location `json:"locations,omitempty"`
|
||||||
Extensions map[string]interface{} `json:"extensions,omitempty"`
|
Extensions map[string]any `json:"extensions,omitempty"`
|
||||||
Rule string `json:"-"`
|
Rule string `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (err *Error) SetFile(file string) {
|
func (err *Error) SetFile(file string) {
|
||||||
@@ -24,7 +24,7 @@ func (err *Error) SetFile(file string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err.Extensions == nil {
|
if err.Extensions == nil {
|
||||||
err.Extensions = map[string]interface{}{}
|
err.Extensions = map[string]any{}
|
||||||
}
|
}
|
||||||
|
|
||||||
err.Extensions["file"] = file
|
err.Extensions["file"] = file
|
||||||
@@ -99,7 +99,7 @@ func (errs List) Is(target error) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (errs List) As(target interface{}) bool {
|
func (errs List) As(target any) bool {
|
||||||
for _, err := range errs {
|
for _, err := range errs {
|
||||||
if errors.As(err, target) {
|
if errors.As(err, target) {
|
||||||
return true
|
return true
|
||||||
@@ -141,7 +141,8 @@ func WrapIfUnwrapped(err error) *Error {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if gqlErr, ok := err.(*Error); ok {
|
gqlErr := &Error{}
|
||||||
|
if errors.As(err, &gqlErr) {
|
||||||
return gqlErr
|
return gqlErr
|
||||||
}
|
}
|
||||||
return &Error{
|
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{
|
return &Error{
|
||||||
Message: fmt.Sprintf(message, args...),
|
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{
|
return &Error{
|
||||||
Message: fmt.Sprintf(message, args...),
|
Message: fmt.Sprintf(message, args...),
|
||||||
Path: path,
|
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 {
|
if pos == nil {
|
||||||
return ErrorLocf(
|
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 {
|
func ErrorLocf(file string, line, col int, message string, args ...any) *Error {
|
||||||
var extensions map[string]interface{}
|
var extensions map[string]any
|
||||||
if file != "" {
|
if file != "" {
|
||||||
extensions = map[string]interface{}{"file": file}
|
extensions = map[string]any{"file": file}
|
||||||
}
|
}
|
||||||
return &Error{
|
return &Error{
|
||||||
Message: fmt.Sprintf(message, args...),
|
Message: fmt.Sprintf(message, args...),
|
||||||
|
|||||||
+79
-22
@@ -2,13 +2,14 @@ package lexer
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"slices"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
"github.com/vektah/gqlparser/v2/ast"
|
"github.com/vektah/gqlparser/v2/ast"
|
||||||
"github.com/vektah/gqlparser/v2/gqlerror"
|
"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 {
|
type Lexer struct {
|
||||||
*ast.Source
|
*ast.Source
|
||||||
// An offset into the string in bytes
|
// 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) {
|
func (s *Lexer) peek() (rune, int) {
|
||||||
return utf8.DecodeRuneInString(s.Input[s.end:])
|
return utf8.DecodeRuneInString(s.Input[s.end:])
|
||||||
}
|
}
|
||||||
@@ -55,7 +56,7 @@ func (s *Lexer) makeValueToken(kind Type, value string) (Token, error) {
|
|||||||
}, nil
|
}, 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
|
column := s.endRunes - s.lineStartRunes + 1
|
||||||
return Token{
|
return Token{
|
||||||
Kind: Invalid,
|
Kind: Invalid,
|
||||||
@@ -122,7 +123,59 @@ func (s *Lexer) ReadToken() (Token, error) {
|
|||||||
case '#':
|
case '#':
|
||||||
return s.readComment()
|
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()
|
return s.readName()
|
||||||
|
|
||||||
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||||
@@ -144,14 +197,16 @@ func (s *Lexer) ReadToken() (Token, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if r == '\'' {
|
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))
|
return s.makeError(`Cannot parse the unexpected character "%s".`, string(r))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ws reads from body starting at startPosition until it finds a non-whitespace
|
// 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() {
|
func (s *Lexer) ws() {
|
||||||
for s.end < len(s.Input) {
|
for s.end < len(s.Input) {
|
||||||
switch s.Input[s.end] {
|
switch s.Input[s.end] {
|
||||||
@@ -189,7 +244,7 @@ func (s *Lexer) ws() {
|
|||||||
|
|
||||||
// readComment from the input
|
// readComment from the input
|
||||||
//
|
//
|
||||||
// #[\u0009\u0020-\uFFFF]*
|
// #[\u0009\u0020-\uFFFF]*.
|
||||||
func (s *Lexer) readComment() (Token, error) {
|
func (s *Lexer) readComment() (Token, error) {
|
||||||
for s.end < len(s.Input) {
|
for s.end < len(s.Input) {
|
||||||
r, w := s.peek()
|
r, w := s.peek()
|
||||||
@@ -256,23 +311,21 @@ func (s *Lexer) readNumber() (Token, error) {
|
|||||||
return s.makeToken(Int)
|
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 {
|
func (s *Lexer) acceptByte(bytes ...uint8) bool {
|
||||||
if s.end >= len(s.Input) {
|
if s.end >= len(s.Input) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, accepted := range bytes {
|
if slices.Contains(bytes, s.Input[s.end]) {
|
||||||
if s.Input[s.end] == accepted {
|
s.end++
|
||||||
s.end++
|
s.endRunes++
|
||||||
s.endRunes++
|
return true
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false
|
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 {
|
func (s *Lexer) acceptDigits() int {
|
||||||
consumed := 0
|
consumed := 0
|
||||||
for s.end < len(s.Input) && s.Input[s.end] >= '0' && s.Input[s.end] <= '9' {
|
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
|
// 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 {
|
func (s *Lexer) describeNext() string {
|
||||||
if s.end < len(s.Input) {
|
if s.end < len(s.Input) {
|
||||||
return `"` + string(s.Input[s.end]) + `"`
|
return `"` + string(s.Input[s.end]) + `"`
|
||||||
@@ -295,7 +348,7 @@ func (s *Lexer) describeNext() string {
|
|||||||
|
|
||||||
// readString from the input
|
// 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) {
|
func (s *Lexer) readString() (Token, error) {
|
||||||
inputLen := len(s.Input)
|
inputLen := len(s.Input)
|
||||||
|
|
||||||
@@ -332,7 +385,8 @@ func (s *Lexer) readString() (Token, error) {
|
|||||||
|
|
||||||
case '"':
|
case '"':
|
||||||
t, err := s.makeToken(String)
|
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.Start--
|
||||||
t.Pos.End++
|
t.Pos.End++
|
||||||
|
|
||||||
@@ -370,7 +424,10 @@ func (s *Lexer) readString() (Token, error) {
|
|||||||
if !ok {
|
if !ok {
|
||||||
s.end++
|
s.end++
|
||||||
s.endRunes++
|
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)
|
buf.WriteRune(r)
|
||||||
s.end += 6
|
s.end += 6
|
||||||
@@ -405,7 +462,7 @@ func (s *Lexer) readString() (Token, error) {
|
|||||||
|
|
||||||
// readBlockString from the input
|
// readBlockString from the input
|
||||||
//
|
//
|
||||||
// """("?"?(\\"""|\\(?!=""")|[^"\\]))*"""
|
// """("?"?(\\"""|\\(?!=""")|[^"\\]))*""".
|
||||||
func (s *Lexer) readBlockString() (Token, error) {
|
func (s *Lexer) readBlockString() (Token, error) {
|
||||||
inputLen := len(s.Input)
|
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 we have at least 3 quotes, use the last 3 as the closing quote
|
||||||
if quoteCount >= 3 {
|
if quoteCount >= 3 {
|
||||||
// Add any extra quotes to the buffer (except the last 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('"')
|
buf.WriteByte('"')
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -508,7 +565,7 @@ func unhex(b string) (v rune, ok bool) {
|
|||||||
|
|
||||||
// readName from the input
|
// readName from the input
|
||||||
//
|
//
|
||||||
// [_A-Za-z][_0-9A-Za-z]*
|
// [_A-Za-z][_0-9A-Za-z]*.
|
||||||
func (s *Lexer) readName() (Token, error) {
|
func (s *Lexer) readName() (Token, error) {
|
||||||
for s.end < len(s.Input) {
|
for s.end < len(s.Input) {
|
||||||
r, w := s.peek()
|
r, w := s.peek()
|
||||||
|
|||||||
+3
-3
@@ -91,7 +91,7 @@ func (p *parser) peek() lexer.Token {
|
|||||||
return p.peekToken
|
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 {
|
if p.err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -165,7 +165,7 @@ func (p *parser) unexpectedToken(tok lexer.Token) {
|
|||||||
p.error(tok, "Unexpected %s", tok.String())
|
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)
|
hasDef := p.skip(start)
|
||||||
if !hasDef {
|
if !hasDef {
|
||||||
return
|
return
|
||||||
@@ -177,7 +177,7 @@ func (p *parser) many(start lexer.Type, end lexer.Type, cb func()) {
|
|||||||
p.next()
|
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)
|
hasDef := p.skip(start)
|
||||||
if !hasDef {
|
if !hasDef {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
+7
-3
@@ -1,9 +1,8 @@
|
|||||||
package parser
|
package parser
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/vektah/gqlparser/v2/lexer"
|
|
||||||
|
|
||||||
. "github.com/vektah/gqlparser/v2/ast" //nolint:staticcheck // bad, yeah
|
. "github.com/vektah/gqlparser/v2/ast" //nolint:staticcheck // bad, yeah
|
||||||
|
"github.com/vektah/gqlparser/v2/lexer"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ParseQuery(source *Source) (*QueryDocument, error) {
|
func ParseQuery(source *Source) (*QueryDocument, error) {
|
||||||
@@ -259,7 +258,12 @@ func (p *parser) parseValueLiteral(isConst bool) *Value {
|
|||||||
p.unexpectedError()
|
p.unexpectedError()
|
||||||
return nil
|
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:
|
case lexer.Int:
|
||||||
kind = IntValue
|
kind = IntValue
|
||||||
case lexer.Float:
|
case lexer.Float:
|
||||||
|
|||||||
+8
-11
@@ -8,11 +8,12 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/agnivade/levenshtein"
|
"github.com/agnivade/levenshtein"
|
||||||
|
|
||||||
"github.com/vektah/gqlparser/v2/ast"
|
"github.com/vektah/gqlparser/v2/ast"
|
||||||
"github.com/vektah/gqlparser/v2/gqlerror"
|
"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) {
|
return func(err *gqlerror.Error) {
|
||||||
err.Message += fmt.Sprintf(msg, args...)
|
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)
|
suggested := SuggestionList(typed, suggestions)
|
||||||
return func(err *gqlerror.Error) {
|
return func(err *gqlerror.Error) {
|
||||||
if len(suggested) > 0 {
|
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)
|
suggested := SuggestionList(typed, suggestions)
|
||||||
return func(err *gqlerror.Error) {
|
return func(err *gqlerror.Error) {
|
||||||
if len(suggested) > 0 {
|
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) {
|
return func(err *gqlerror.Error) {
|
||||||
err.Message += " Did you mean " + fmt.Sprintf(suggestion, args...) + "?"
|
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) {
|
func calcThreshold(a string) (threshold int) {
|
||||||
// the logic is copied from here
|
// the logic is copied from here
|
||||||
// https://github.com/graphql/graphql-js/blob/47bd8c8897c72d3efc17ecb1599a95cee6bac5e8/src/jsutils/suggestionList.ts#L14
|
// https://github.com/graphql/graphql-js/blob/47bd8c8897c72d3efc17ecb1599a95cee6bac5e8/src/jsutils/suggestionList.ts#L14
|
||||||
threshold = int(math.Floor(float64(len(a))*0.4) + 1)
|
threshold = max(int(math.Floor(float64(len(a))*0.4)+1), 1)
|
||||||
|
return threshold
|
||||||
if threshold < 1 {
|
|
||||||
threshold = 1
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Computes the lexical distance between strings A and B.
|
// 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
|
// as a single edit which helps identify mis-cased values with an edit distance
|
||||||
// of 1.
|
// 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 {
|
func lexicalDistance(a, b string) int {
|
||||||
if a == b {
|
if a == b {
|
||||||
return 0
|
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 {
|
for _, dir := range directives {
|
||||||
def := w.Schema.Directives[dir.Name]
|
def := w.Schema.Directives[dir.Name]
|
||||||
dir.Definition = def
|
dir.Definition = def
|
||||||
@@ -182,6 +186,8 @@ func (w *Walker) walkValue(value *ast.Value) {
|
|||||||
fieldDef := value.Definition.Fields.ForName(child.Name)
|
fieldDef := value.Definition.Fields.ForName(child.Name)
|
||||||
if fieldDef != nil {
|
if fieldDef != nil {
|
||||||
child.Value.ExpectedType = fieldDef.Type
|
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()]
|
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) {
|
func (w *Walker) walkArgument(argDef *ast.ArgumentDefinition, arg *ast.Argument) {
|
||||||
if argDef != nil {
|
if argDef != nil {
|
||||||
arg.Value.ExpectedType = argDef.Type
|
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()]
|
arg.Value.Definition = w.Schema.Types[argDef.Type.Name()]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+19
-9
@@ -3,10 +3,8 @@ package rules
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/vektah/gqlparser/v2/ast"
|
"github.com/vektah/gqlparser/v2/ast"
|
||||||
|
|
||||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
. "github.com/vektah/gqlparser/v2/validator/core"
|
||||||
)
|
)
|
||||||
@@ -17,12 +15,24 @@ func ruleFuncFieldsOnCorrectType(observers *Events, addError AddErrFunc, disable
|
|||||||
return
|
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 !disableSuggestion {
|
||||||
if suggestedTypeNames := getSuggestedTypeNames(walker, field.ObjectDefinition, field.Name); suggestedTypeNames != nil {
|
if suggestedTypeNames := getSuggestedTypeNames(
|
||||||
message += " Did you mean to use an inline fragment on " + QuotedOrList(suggestedTypeNames...) + "?"
|
walker,
|
||||||
} else if suggestedFieldNames := getSuggestedFieldNames(field.ObjectDefinition, field.Name); suggestedFieldNames != nil {
|
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...) + "?"
|
message += " Did you mean " + QuotedOrList(suggestedFieldNames...) + "?"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -89,7 +99,7 @@ func getSuggestedTypeNames(walker *Walker, parent *ast.Definition, name string)
|
|||||||
if diff != 0 {
|
if diff != 0 {
|
||||||
return diff < 0
|
return diff < 0
|
||||||
}
|
}
|
||||||
return strings.Compare(typeA, typeB) < 0
|
return typeA < typeB
|
||||||
})
|
})
|
||||||
|
|
||||||
return suggestedTypes
|
return suggestedTypes
|
||||||
@@ -99,8 +109,8 @@ func getSuggestedTypeNames(walker *Walker, parent *ast.Definition, name string)
|
|||||||
// where max is set to the slice’s length,
|
// where max is set to the slice’s length,
|
||||||
// we ensure that appending elements results
|
// we ensure that appending elements results
|
||||||
// in a slice backed by a distinct array.
|
// in a slice backed by a distinct array.
|
||||||
// This method prevents the shared array issue
|
// This method prevents the shared array issue.
|
||||||
func concatSlice(first []string, second []string) []string {
|
func concatSlice(first, second []string) []string {
|
||||||
n := len(first)
|
n := len(first)
|
||||||
return append(first[:n:n], second...)
|
return append(first[:n:n], second...)
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
Vendored
+11
-4
@@ -4,7 +4,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/vektah/gqlparser/v2/ast"
|
"github.com/vektah/gqlparser/v2/ast"
|
||||||
|
|
||||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
. "github.com/vektah/gqlparser/v2/validator/core"
|
||||||
)
|
)
|
||||||
@@ -18,7 +17,10 @@ var FragmentsOnCompositeTypesRule = Rule{
|
|||||||
return
|
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(
|
addError(
|
||||||
Message("%s", message),
|
Message("%s", message),
|
||||||
@@ -27,11 +29,16 @@ var FragmentsOnCompositeTypesRule = Rule{
|
|||||||
})
|
})
|
||||||
|
|
||||||
observers.OnFragment(func(walker *Walker, fragment *ast.FragmentDefinition) {
|
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
|
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(
|
addError(
|
||||||
Message("%s", message),
|
Message("%s", message),
|
||||||
|
|||||||
+12
-3
@@ -2,7 +2,6 @@ package rules
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/vektah/gqlparser/v2/ast"
|
"github.com/vektah/gqlparser/v2/ast"
|
||||||
|
|
||||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
. "github.com/vektah/gqlparser/v2/validator/core"
|
||||||
)
|
)
|
||||||
@@ -21,7 +20,12 @@ func ruleFuncKnownArgumentNames(observers *Events, addError AddErrFunc, disableS
|
|||||||
|
|
||||||
if disableSuggestion {
|
if disableSuggestion {
|
||||||
addError(
|
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),
|
At(field.Position),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
@@ -30,7 +34,12 @@ func ruleFuncKnownArgumentNames(observers *Events, addError AddErrFunc, disableS
|
|||||||
suggestions = append(suggestions, argDef.Name)
|
suggestions = append(suggestions, argDef.Name)
|
||||||
}
|
}
|
||||||
addError(
|
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),
|
SuggestListQuoted("Did you mean", arg.Name, suggestions),
|
||||||
At(field.Position),
|
At(field.Position),
|
||||||
)
|
)
|
||||||
|
|||||||
+9
-6
@@ -1,8 +1,9 @@
|
|||||||
package rules
|
package rules
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/vektah/gqlparser/v2/ast"
|
"slices"
|
||||||
|
|
||||||
|
"github.com/vektah/gqlparser/v2/ast"
|
||||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
. "github.com/vektah/gqlparser/v2/validator/core"
|
||||||
)
|
)
|
||||||
@@ -25,10 +26,8 @@ var KnownDirectivesRule = Rule{
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, loc := range directive.Definition.Locations {
|
if slices.Contains(directive.Definition.Locations, directive.Location) {
|
||||||
if loc == directive.Location {
|
return
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// position must be exists if directive.Definition != nil
|
// position must be exists if directive.Definition != nil
|
||||||
@@ -40,7 +39,11 @@ var KnownDirectivesRule = Rule{
|
|||||||
|
|
||||||
if !seen[tmp] {
|
if !seen[tmp] {
|
||||||
addError(
|
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),
|
At(directive.Position),
|
||||||
)
|
)
|
||||||
seen[tmp] = true
|
seen[tmp] = true
|
||||||
|
|||||||
-1
@@ -2,7 +2,6 @@ package rules
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/vektah/gqlparser/v2/ast"
|
"github.com/vektah/gqlparser/v2/ast"
|
||||||
|
|
||||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
. "github.com/vektah/gqlparser/v2/validator/core"
|
||||||
)
|
)
|
||||||
|
|||||||
-1
@@ -4,7 +4,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/vektah/gqlparser/v2/ast"
|
"github.com/vektah/gqlparser/v2/ast"
|
||||||
|
|
||||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
. "github.com/vektah/gqlparser/v2/validator/core"
|
||||||
)
|
)
|
||||||
|
|||||||
-1
@@ -2,7 +2,6 @@ package rules
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/vektah/gqlparser/v2/ast"
|
"github.com/vektah/gqlparser/v2/ast"
|
||||||
|
|
||||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
. "github.com/vektah/gqlparser/v2/validator/core"
|
||||||
)
|
)
|
||||||
|
|||||||
Generated
Vendored
-1
@@ -2,7 +2,6 @@ package rules
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/vektah/gqlparser/v2/ast"
|
"github.com/vektah/gqlparser/v2/ast"
|
||||||
|
|
||||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
. "github.com/vektah/gqlparser/v2/validator/core"
|
||||||
)
|
)
|
||||||
|
|||||||
+10
-3
@@ -2,7 +2,6 @@ package rules
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/vektah/gqlparser/v2/ast"
|
"github.com/vektah/gqlparser/v2/ast"
|
||||||
|
|
||||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
. "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 {
|
for _, child := range selectionSet {
|
||||||
if field, ok := child.(*ast.Field); ok {
|
if field, ok := child.(*ast.Field); ok {
|
||||||
if checkDepthField(field, visitedFragments, depth) {
|
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)
|
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
|
fragmentName := fragmentSpread.Name
|
||||||
if visited, ok := visitedFragments[fragmentName]; ok && visited {
|
if visited, ok := visitedFragments[fragmentName]; ok && visited {
|
||||||
// Fragment cycles are handled by `NoFragmentCyclesRule`.
|
// Fragment cycles are handled by `NoFragmentCyclesRule`.
|
||||||
|
|||||||
+5
-2
@@ -5,7 +5,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/vektah/gqlparser/v2/ast"
|
"github.com/vektah/gqlparser/v2/ast"
|
||||||
|
|
||||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
. "github.com/vektah/gqlparser/v2/validator/core"
|
||||||
)
|
)
|
||||||
@@ -55,7 +54,11 @@ var NoFragmentCyclesRule = Rule{
|
|||||||
via = fmt.Sprintf(" via %s", strings.Join(fragmentNames, ", "))
|
via = fmt.Sprintf(" via %s", strings.Join(fragmentNames, ", "))
|
||||||
}
|
}
|
||||||
addError(
|
addError(
|
||||||
Message(`Cannot spread fragment "%s" within itself%s.`, spreadName, via),
|
Message(
|
||||||
|
`Cannot spread fragment "%s" within itself%s.`,
|
||||||
|
spreadName,
|
||||||
|
via,
|
||||||
|
),
|
||||||
At(spreadNode.Position),
|
At(spreadNode.Position),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-3
@@ -2,7 +2,6 @@ package rules
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/vektah/gqlparser/v2/ast"
|
"github.com/vektah/gqlparser/v2/ast"
|
||||||
|
|
||||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
. "github.com/vektah/gqlparser/v2/validator/core"
|
||||||
)
|
)
|
||||||
@@ -11,13 +10,18 @@ var NoUndefinedVariablesRule = Rule{
|
|||||||
Name: "NoUndefinedVariables",
|
Name: "NoUndefinedVariables",
|
||||||
RuleFunc: func(observers *Events, addError AddErrFunc) {
|
RuleFunc: func(observers *Events, addError AddErrFunc) {
|
||||||
observers.OnValue(func(walker *Walker, value *ast.Value) {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if walker.CurrentOperation.Name != "" {
|
if walker.CurrentOperation.Name != "" {
|
||||||
addError(
|
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),
|
At(value.Position),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
-1
@@ -2,7 +2,6 @@ package rules
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/vektah/gqlparser/v2/ast"
|
"github.com/vektah/gqlparser/v2/ast"
|
||||||
|
|
||||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
. "github.com/vektah/gqlparser/v2/validator/core"
|
||||||
)
|
)
|
||||||
|
|||||||
+5
-2
@@ -2,7 +2,6 @@ package rules
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/vektah/gqlparser/v2/ast"
|
"github.com/vektah/gqlparser/v2/ast"
|
||||||
|
|
||||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
. "github.com/vektah/gqlparser/v2/validator/core"
|
||||||
)
|
)
|
||||||
@@ -18,7 +17,11 @@ var NoUnusedVariablesRule = Rule{
|
|||||||
|
|
||||||
if operation.Name != "" {
|
if operation.Name != "" {
|
||||||
addError(
|
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),
|
At(varDef.Position),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Generated
Vendored
+109
-29
@@ -6,7 +6,6 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
"github.com/vektah/gqlparser/v2/ast"
|
"github.com/vektah/gqlparser/v2/ast"
|
||||||
|
|
||||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
. "github.com/vektah/gqlparser/v2/validator/core"
|
||||||
)
|
)
|
||||||
@@ -82,7 +81,8 @@ var OverlappingFieldsCanBeMergedRule = Rule{
|
|||||||
})
|
})
|
||||||
observers.OnField(func(walker *Walker, field *ast.Field) {
|
observers.OnField(func(walker *Walker, field *ast.Field) {
|
||||||
if walker.CurrentOperation == nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
m.walker = walker
|
m.walker = walker
|
||||||
@@ -112,7 +112,11 @@ type pairSet struct {
|
|||||||
data map[string]map[string]bool
|
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) {
|
add := func(a *ast.FragmentSpread, b *ast.FragmentSpread) {
|
||||||
m := pairSet.data[a.Name]
|
m := pairSet.data[a.Name]
|
||||||
if m == nil {
|
if m == nil {
|
||||||
@@ -125,7 +129,11 @@ func (pairSet *pairSet) Add(a *ast.FragmentSpread, b *ast.FragmentSpread, areMut
|
|||||||
add(b, a)
|
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]
|
am, ok := pairSet.data[a.Name]
|
||||||
if !ok {
|
if !ok {
|
||||||
return false
|
return false
|
||||||
@@ -224,7 +232,11 @@ func (m *ConflictMessage) addFieldsConflictMessage(addError AddErrFunc) {
|
|||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
m.String(&buf)
|
m.String(&buf)
|
||||||
addError(
|
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),
|
At(m.Position),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -240,7 +252,9 @@ type overlappingFieldsCanBeMergedManager struct {
|
|||||||
comparedFragments map[string]bool
|
comparedFragments map[string]bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *overlappingFieldsCanBeMergedManager) findConflictsWithinSelectionSet(selectionSet ast.SelectionSet) []*ConflictMessage {
|
func (m *overlappingFieldsCanBeMergedManager) findConflictsWithinSelectionSet(
|
||||||
|
selectionSet ast.SelectionSet,
|
||||||
|
) []*ConflictMessage {
|
||||||
if len(selectionSet) == 0 {
|
if len(selectionSet) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -271,7 +285,12 @@ func (m *overlappingFieldsCanBeMergedManager) findConflictsWithinSelectionSet(se
|
|||||||
return conflicts.Conflicts
|
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] {
|
if m.comparedFragments[fragmentSpread.Name] {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -299,11 +318,21 @@ func (m *overlappingFieldsCanBeMergedManager) collectConflictsBetweenFieldsAndFr
|
|||||||
if fragmentSpread.Name == baseFragmentSpread.Name {
|
if fragmentSpread.Name == baseFragmentSpread.Name {
|
||||||
continue
|
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)
|
var check func(fragmentSpreadA *ast.FragmentSpread, fragmentSpreadB *ast.FragmentSpread)
|
||||||
check = func(fragmentSpreadA *ast.FragmentSpread, fragmentSpreadB *ast.FragmentSpread) {
|
check = func(fragmentSpreadA *ast.FragmentSpread, fragmentSpreadB *ast.FragmentSpread) {
|
||||||
if fragmentSpreadA.Name == fragmentSpreadB.Name {
|
if fragmentSpreadA.Name == fragmentSpreadB.Name {
|
||||||
@@ -322,8 +351,12 @@ func (m *overlappingFieldsCanBeMergedManager) collectConflictsBetweenFragments(c
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
fieldsMapA, fragmentSpreadsA := getFieldsAndFragmentNames(fragmentSpreadA.Definition.SelectionSet)
|
fieldsMapA, fragmentSpreadsA := getFieldsAndFragmentNames(
|
||||||
fieldsMapB, fragmentSpreadsB := getFieldsAndFragmentNames(fragmentSpreadB.Definition.SelectionSet)
|
fragmentSpreadA.Definition.SelectionSet,
|
||||||
|
)
|
||||||
|
fieldsMapB, fragmentSpreadsB := getFieldsAndFragmentNames(
|
||||||
|
fragmentSpreadB.Definition.SelectionSet,
|
||||||
|
)
|
||||||
|
|
||||||
// (F) First, collect all conflicts between these two collections of fields
|
// (F) First, collect all conflicts between these two collections of fields
|
||||||
// (not including any nested fragments).
|
// (not including any nested fragments).
|
||||||
@@ -344,7 +377,11 @@ func (m *overlappingFieldsCanBeMergedManager) collectConflictsBetweenFragments(c
|
|||||||
check(fragmentSpreadA, fragmentSpreadB)
|
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
|
var conflicts conflictMessageContainer
|
||||||
|
|
||||||
fieldsMapA, fragmentSpreadsA := getFieldsAndFragmentNames(selectionSetA)
|
fieldsMapA, fragmentSpreadsA := getFieldsAndFragmentNames(selectionSetA)
|
||||||
@@ -357,14 +394,24 @@ func (m *overlappingFieldsCanBeMergedManager) findConflictsBetweenSubSelectionSe
|
|||||||
// those referenced by each fragment name associated with the second.
|
// those referenced by each fragment name associated with the second.
|
||||||
for _, fragmentSpread := range fragmentSpreadsB {
|
for _, fragmentSpread := range fragmentSpreadsB {
|
||||||
m.comparedFragments = make(map[string]bool)
|
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
|
// (I) Then collect conflicts between the second collection of fields and
|
||||||
// those referenced by each fragment name associated with the first.
|
// those referenced by each fragment name associated with the first.
|
||||||
for _, fragmentSpread := range fragmentSpreadsA {
|
for _, fragmentSpread := range fragmentSpreadsA {
|
||||||
m.comparedFragments = make(map[string]bool)
|
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
|
// (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.
|
// names to each item in the second set of names.
|
||||||
for _, fragmentSpreadA := range fragmentSpreadsA {
|
for _, fragmentSpreadA := range fragmentSpreadsA {
|
||||||
for _, fragmentSpreadB := range fragmentSpreadsB {
|
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
|
return &conflicts
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *overlappingFieldsCanBeMergedManager) collectConflictsWithin(conflicts *conflictMessageContainer, fieldsMap *sequentialFieldsMap) {
|
func (m *overlappingFieldsCanBeMergedManager) collectConflictsWithin(
|
||||||
|
conflicts *conflictMessageContainer,
|
||||||
|
fieldsMap *sequentialFieldsMap,
|
||||||
|
) {
|
||||||
for _, fields := range fieldsMap.Iterator() {
|
for _, fields := range fieldsMap.Iterator() {
|
||||||
for idx, fieldA := range fields {
|
for idx, fieldA := range fields {
|
||||||
for _, fieldB := range fields[idx+1:] {
|
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() {
|
for _, fieldsEntryA := range fieldsMapA.KeyValueIterator() {
|
||||||
fieldsB, ok := fieldsMapB.Get(fieldsEntryA.ResponseName)
|
fieldsB, ok := fieldsMapB.Get(fieldsEntryA.ResponseName)
|
||||||
if !ok {
|
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 {
|
if fieldA.ObjectDefinition == nil || fieldB.ObjectDefinition == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -437,8 +501,12 @@ func (m *overlappingFieldsCanBeMergedManager) findConflict(parentFieldsAreMutual
|
|||||||
if fieldA.Name != fieldB.Name {
|
if fieldA.Name != fieldB.Name {
|
||||||
return &ConflictMessage{
|
return &ConflictMessage{
|
||||||
ResponseName: fieldNameA,
|
ResponseName: fieldNameA,
|
||||||
Message: fmt.Sprintf(`"%s" and "%s" are different fields`, fieldA.Name, fieldB.Name),
|
Message: fmt.Sprintf(
|
||||||
Position: fieldB.Position,
|
`"%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{
|
return &ConflictMessage{
|
||||||
ResponseName: fieldNameA,
|
ResponseName: fieldNameA,
|
||||||
Message: fmt.Sprintf(`they return conflicting types "%s" and "%s"`, fieldA.Definition.Type.String(), fieldB.Definition.Type.String()),
|
Message: fmt.Sprintf(
|
||||||
Position: fieldB.Position,
|
`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
|
// Collect and compare sub-fields. Use the same "visited fragment names" list
|
||||||
// for both collections so fields in a fragment reference are never
|
// for both collections so fields in a fragment reference are never
|
||||||
// compared to themselves.
|
// compared to themselves.
|
||||||
conflicts := m.findConflictsBetweenSubSelectionSets(areMutuallyExclusive, fieldA.SelectionSet, fieldB.SelectionSet)
|
conflicts := m.findConflictsBetweenSubSelectionSets(
|
||||||
|
areMutuallyExclusive,
|
||||||
|
fieldA.SelectionSet,
|
||||||
|
fieldB.SelectionSet,
|
||||||
|
)
|
||||||
if conflicts == nil {
|
if conflicts == nil {
|
||||||
return 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) {
|
if len(args1) != len(args2) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -493,7 +570,7 @@ func sameArguments(args1 []*ast.Argument, args2 []*ast.Argument) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func sameValue(value1 *ast.Value, value2 *ast.Value) bool {
|
func sameValue(value1, value2 *ast.Value) bool {
|
||||||
if value1.Kind != value2.Kind {
|
if value1.Kind != value2.Kind {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -503,7 +580,7 @@ func sameValue(value1 *ast.Value, value2 *ast.Value) bool {
|
|||||||
return true
|
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 type1.Elem != nil {
|
||||||
if type2.Elem != nil {
|
if type2.Elem != nil {
|
||||||
return doTypesConflict(walker, type1.Elem, type2.Elem)
|
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]
|
t1 := walker.Schema.Types[type1.NamedType]
|
||||||
t2 := walker.Schema.Types[type2.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 t1.Name != t2.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func getFieldsAndFragmentNames(selectionSet ast.SelectionSet) (*sequentialFieldsMap, []*ast.FragmentSpread) {
|
func getFieldsAndFragmentNames(
|
||||||
|
selectionSet ast.SelectionSet,
|
||||||
|
) (*sequentialFieldsMap, []*ast.FragmentSpread) {
|
||||||
fieldsMap := sequentialFieldsMap{
|
fieldsMap := sequentialFieldsMap{
|
||||||
data: make(map[string][]*ast.Field),
|
data: make(map[string][]*ast.Field),
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
Vendored
+21
-8
@@ -2,7 +2,6 @@ package rules
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/vektah/gqlparser/v2/ast"
|
"github.com/vektah/gqlparser/v2/ast"
|
||||||
|
|
||||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
. "github.com/vektah/gqlparser/v2/validator/core"
|
||||||
)
|
)
|
||||||
@@ -49,7 +48,11 @@ var PossibleFragmentSpreadsRule = Rule{
|
|||||||
observers.OnInlineFragment(func(walker *Walker, inlineFragment *ast.InlineFragment) {
|
observers.OnInlineFragment(func(walker *Walker, inlineFragment *ast.InlineFragment) {
|
||||||
validate(walker, inlineFragment.ObjectDefinition, inlineFragment.TypeCondition, func() {
|
validate(walker, inlineFragment.ObjectDefinition, inlineFragment.TypeCondition, func() {
|
||||||
addError(
|
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),
|
At(inlineFragment.Position),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@@ -59,12 +62,22 @@ var PossibleFragmentSpreadsRule = Rule{
|
|||||||
if fragmentSpread.Definition == nil {
|
if fragmentSpread.Definition == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
validate(walker, fragmentSpread.ObjectDefinition, fragmentSpread.Definition.TypeCondition, func() {
|
validate(
|
||||||
addError(
|
walker,
|
||||||
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),
|
fragmentSpread.ObjectDefinition,
|
||||||
At(fragmentSpread.Position),
|
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.
|
// GetInner returns the internal rule map.
|
||||||
// If the map is not initialized, it returns an empty 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 {
|
func (r *Rules) GetInner() map[string]core.RuleFunc {
|
||||||
if r == nil {
|
if r == nil {
|
||||||
return nil // impossible nonsense, hopefully
|
return nil // impossible nonsense, hopefully
|
||||||
@@ -84,7 +85,13 @@ func (r *Rules) GetInner() map[string]core.RuleFunc {
|
|||||||
if r.rules == nil {
|
if r.rules == nil {
|
||||||
return make(map[string]core.RuleFunc)
|
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.
|
// RemoveRule removes a rule with the specified name from the rule set.
|
||||||
|
|||||||
+10
-3
@@ -2,7 +2,6 @@ package rules
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/vektah/gqlparser/v2/ast"
|
"github.com/vektah/gqlparser/v2/ast"
|
||||||
|
|
||||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
. "github.com/vektah/gqlparser/v2/validator/core"
|
||||||
)
|
)
|
||||||
@@ -22,14 +21,22 @@ var ScalarLeafsRule = Rule{
|
|||||||
|
|
||||||
if fieldType.IsLeafType() && len(field.SelectionSet) > 0 {
|
if fieldType.IsLeafType() && len(field.SelectionSet) > 0 {
|
||||||
addError(
|
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),
|
At(field.Position),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !fieldType.IsLeafType() && len(field.SelectionSet) == 0 {
|
if !fieldType.IsLeafType() && len(field.SelectionSet) == 0 {
|
||||||
addError(
|
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),
|
Suggestf(`"%s { ... }"`, field.Name),
|
||||||
At(field.Position),
|
At(field.Position),
|
||||||
)
|
)
|
||||||
|
|||||||
Generated
Vendored
-1
@@ -5,7 +5,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/vektah/gqlparser/v2/ast"
|
"github.com/vektah/gqlparser/v2/ast"
|
||||||
|
|
||||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
. "github.com/vektah/gqlparser/v2/validator/core"
|
||||||
)
|
)
|
||||||
|
|||||||
-1
@@ -2,7 +2,6 @@ package rules
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/vektah/gqlparser/v2/ast"
|
"github.com/vektah/gqlparser/v2/ast"
|
||||||
|
|
||||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
. "github.com/vektah/gqlparser/v2/validator/core"
|
||||||
)
|
)
|
||||||
|
|||||||
Generated
Vendored
+4
-2
@@ -2,7 +2,6 @@ package rules
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/vektah/gqlparser/v2/ast"
|
"github.com/vektah/gqlparser/v2/ast"
|
||||||
|
|
||||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
. "github.com/vektah/gqlparser/v2/validator/core"
|
||||||
)
|
)
|
||||||
@@ -16,7 +15,10 @@ var UniqueDirectivesPerLocationRule = Rule{
|
|||||||
for _, dir := range directives {
|
for _, dir := range directives {
|
||||||
if dir.Name != "repeatable" && seen[dir.Name] {
|
if dir.Name != "repeatable" && seen[dir.Name] {
|
||||||
addError(
|
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),
|
At(dir.Position),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
-1
@@ -2,7 +2,6 @@ package rules
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/vektah/gqlparser/v2/ast"
|
"github.com/vektah/gqlparser/v2/ast"
|
||||||
|
|
||||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
. "github.com/vektah/gqlparser/v2/validator/core"
|
||||||
)
|
)
|
||||||
|
|||||||
Generated
Vendored
-1
@@ -2,7 +2,6 @@ package rules
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/vektah/gqlparser/v2/ast"
|
"github.com/vektah/gqlparser/v2/ast"
|
||||||
|
|
||||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
. "github.com/vektah/gqlparser/v2/validator/core"
|
||||||
)
|
)
|
||||||
|
|||||||
-1
@@ -2,7 +2,6 @@ package rules
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/vektah/gqlparser/v2/ast"
|
"github.com/vektah/gqlparser/v2/ast"
|
||||||
|
|
||||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
. "github.com/vektah/gqlparser/v2/validator/core"
|
||||||
)
|
)
|
||||||
|
|||||||
-1
@@ -2,7 +2,6 @@ package rules
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/vektah/gqlparser/v2/ast"
|
"github.com/vektah/gqlparser/v2/ast"
|
||||||
|
|
||||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
. "github.com/vektah/gqlparser/v2/validator/core"
|
||||||
)
|
)
|
||||||
|
|||||||
+78
-17
@@ -6,7 +6,6 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/vektah/gqlparser/v2/ast"
|
"github.com/vektah/gqlparser/v2/ast"
|
||||||
|
|
||||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
. "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 {
|
if value.Kind == ast.NullValue && value.ExpectedType.NonNull {
|
||||||
addError(
|
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),
|
At(value.Position),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -66,13 +69,21 @@ func ruleFuncValuesOfCorrectType(observers *Events, addError AddErrFunc, disable
|
|||||||
if value.Definition.Kind == ast.Enum {
|
if value.Definition.Kind == ast.Enum {
|
||||||
if disableSuggestion {
|
if disableSuggestion {
|
||||||
addError(
|
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),
|
At(value.Position),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
rawValStr := fmt.Sprint(rawVal)
|
rawValStr := fmt.Sprint(rawVal)
|
||||||
addError(
|
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),
|
SuggestListQuoted("Did you mean the enum value", rawValStr, possibleEnums),
|
||||||
At(value.Position),
|
At(value.Position),
|
||||||
)
|
)
|
||||||
@@ -92,20 +103,32 @@ func ruleFuncValuesOfCorrectType(observers *Events, addError AddErrFunc, disable
|
|||||||
rawValStr := fmt.Sprint(rawVal)
|
rawValStr := fmt.Sprint(rawVal)
|
||||||
addError(
|
addError(
|
||||||
unexpectedTypeMessageOnly(value),
|
unexpectedTypeMessageOnly(value),
|
||||||
SuggestListUnquoted("Did you mean the enum value", rawValStr, possibleEnums),
|
SuggestListUnquoted(
|
||||||
|
"Did you mean the enum value",
|
||||||
|
rawValStr,
|
||||||
|
possibleEnums,
|
||||||
|
),
|
||||||
At(value.Position),
|
At(value.Position),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else if value.Definition.EnumValues.ForName(value.Raw) == nil {
|
} else if value.Definition.EnumValues.ForName(value.Raw) == nil {
|
||||||
if disableSuggestion {
|
if disableSuggestion {
|
||||||
addError(
|
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),
|
At(value.Position),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
rawValStr := fmt.Sprint(rawVal)
|
rawValStr := fmt.Sprint(rawVal)
|
||||||
addError(
|
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),
|
SuggestListQuoted("Did you mean the enum value", rawValStr, possibleEnums),
|
||||||
At(value.Position),
|
At(value.Position),
|
||||||
)
|
)
|
||||||
@@ -124,7 +147,12 @@ func ruleFuncValuesOfCorrectType(observers *Events, addError AddErrFunc, disable
|
|||||||
fieldValue := value.Children.ForName(field.Name)
|
fieldValue := value.Children.ForName(field.Name)
|
||||||
if fieldValue == nil && field.DefaultValue == nil {
|
if fieldValue == nil && field.DefaultValue == nil {
|
||||||
addError(
|
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),
|
At(value.Position),
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
@@ -137,7 +165,10 @@ func ruleFuncValuesOfCorrectType(observers *Events, addError AddErrFunc, disable
|
|||||||
func() {
|
func() {
|
||||||
if len(value.Children) != 1 {
|
if len(value.Children) != 1 {
|
||||||
addError(
|
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),
|
At(value.Position),
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
@@ -147,7 +178,11 @@ func ruleFuncValuesOfCorrectType(observers *Events, addError AddErrFunc, disable
|
|||||||
isNullLiteral := fieldValue == nil || fieldValue.Kind == ast.NullValue
|
isNullLiteral := fieldValue == nil || fieldValue.Kind == ast.NullValue
|
||||||
if isNullLiteral {
|
if isNullLiteral {
|
||||||
addError(
|
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),
|
At(fieldValue.Position),
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
@@ -159,7 +194,11 @@ func ruleFuncValuesOfCorrectType(observers *Events, addError AddErrFunc, disable
|
|||||||
isNullableVariable := !fieldValue.VariableDefinition.Type.NonNull
|
isNullableVariable := !fieldValue.VariableDefinition.Type.NonNull
|
||||||
if isNullableVariable {
|
if isNullableVariable {
|
||||||
addError(
|
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),
|
At(fieldValue.Position),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -172,7 +211,11 @@ func ruleFuncValuesOfCorrectType(observers *Events, addError AddErrFunc, disable
|
|||||||
if value.Definition.Fields.ForName(fieldValue.Name) == nil {
|
if value.Definition.Fields.ForName(fieldValue.Name) == nil {
|
||||||
if disableSuggestion {
|
if disableSuggestion {
|
||||||
addError(
|
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),
|
At(fieldValue.Position),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
@@ -182,7 +225,11 @@ func ruleFuncValuesOfCorrectType(observers *Events, addError AddErrFunc, disable
|
|||||||
}
|
}
|
||||||
|
|
||||||
addError(
|
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),
|
SuggestListQuoted("Did you mean", fieldValue.Name, suggestions),
|
||||||
At(fieldValue.Position),
|
At(fieldValue.Position),
|
||||||
)
|
)
|
||||||
@@ -223,7 +270,12 @@ func unexpectedTypeMessage(addError AddErrFunc, v *ast.Value) {
|
|||||||
func unexpectedTypeMessageOnly(v *ast.Value) ErrorOption {
|
func unexpectedTypeMessageOnly(v *ast.Value) ErrorOption {
|
||||||
switch v.ExpectedType.String() {
|
switch v.ExpectedType.String() {
|
||||||
case "Int", "Int!":
|
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 32-bit signed integer value: %s`, v.String())
|
||||||
}
|
}
|
||||||
return Message(`Int cannot represent non-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!":
|
case "ID", "ID!":
|
||||||
return Message(`ID cannot represent a non-string and non-integer value: %s`, v.String())
|
return Message(`ID cannot represent a non-string and non-integer value: %s`, v.String())
|
||||||
// case "Enum":
|
// 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:
|
default:
|
||||||
if v.Definition.Kind == ast.Enum {
|
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 (
|
import (
|
||||||
"github.com/vektah/gqlparser/v2/ast"
|
"github.com/vektah/gqlparser/v2/ast"
|
||||||
|
|
||||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
. "github.com/vektah/gqlparser/v2/validator/core"
|
||||||
)
|
)
|
||||||
|
|||||||
Generated
Vendored
+10
-3
@@ -2,7 +2,6 @@ package rules
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/vektah/gqlparser/v2/ast"
|
"github.com/vektah/gqlparser/v2/ast"
|
||||||
|
|
||||||
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
//nolint:staticcheck // Validator rules each use dot imports for convenience.
|
||||||
. "github.com/vektah/gqlparser/v2/validator/core"
|
. "github.com/vektah/gqlparser/v2/validator/core"
|
||||||
)
|
)
|
||||||
@@ -11,7 +10,9 @@ var VariablesInAllowedPositionRule = Rule{
|
|||||||
Name: "VariablesInAllowedPosition",
|
Name: "VariablesInAllowedPosition",
|
||||||
RuleFunc: func(observers *Events, addError AddErrFunc) {
|
RuleFunc: func(observers *Events, addError AddErrFunc) {
|
||||||
observers.OnValue(func(walker *Walker, value *ast.Value) {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -19,12 +20,18 @@ var VariablesInAllowedPositionRule = Rule{
|
|||||||
|
|
||||||
// todo: move me into walk
|
// todo: move me into walk
|
||||||
// If there is a default non nullable types can be null
|
// 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 {
|
if value.ExpectedType.NonNull {
|
||||||
tmp.NonNull = false
|
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) {
|
if !value.VariableDefinition.Type.IsCompatible(&tmp) {
|
||||||
addError(
|
addError(
|
||||||
Message(
|
Message(
|
||||||
|
|||||||
+177
-53
@@ -1,6 +1,7 @@
|
|||||||
package validator
|
package validator
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"slices"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -48,7 +49,13 @@ func ValidateSchemaDocument(sd *SchemaDocument) (*Schema, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if def.Kind != ext.Kind {
|
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...)
|
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
|
// version of gqlparser, in which case they're in trouble
|
||||||
// anyway.
|
// anyway.
|
||||||
default:
|
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]
|
schema.Directives[dir.Name] = sd.Directives[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(sd.Schema) > 1 {
|
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 {
|
if len(sd.Schema) == 1 {
|
||||||
@@ -110,7 +124,12 @@ func ValidateSchemaDocument(sd *SchemaDocument) (*Schema, error) {
|
|||||||
for _, entrypoint := range sd.Schema[0].OperationTypes {
|
for _, entrypoint := range sd.Schema[0].OperationTypes {
|
||||||
def := schema.Types[entrypoint.Type]
|
def := schema.Types[entrypoint.Type]
|
||||||
if def == nil {
|
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 {
|
switch entrypoint.Operation {
|
||||||
case Query:
|
case Query:
|
||||||
@@ -121,7 +140,12 @@ func ValidateSchemaDocument(sd *SchemaDocument) (*Schema, error) {
|
|||||||
schema.Subscription = def
|
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
|
return nil, err
|
||||||
}
|
}
|
||||||
schema.SchemaDirectives = append(schema.SchemaDirectives, sd.Schema[0].Directives...)
|
schema.SchemaDirectives = append(schema.SchemaDirectives, sd.Schema[0].Directives...)
|
||||||
@@ -131,7 +155,12 @@ func ValidateSchemaDocument(sd *SchemaDocument) (*Schema, error) {
|
|||||||
for _, entrypoint := range ext.OperationTypes {
|
for _, entrypoint := range ext.OperationTypes {
|
||||||
def := schema.Types[entrypoint.Type]
|
def := schema.Types[entrypoint.Type]
|
||||||
if def == nil {
|
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 {
|
switch entrypoint.Operation {
|
||||||
case Query:
|
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))
|
return gqlerror.ErrorPosf(def.Position, "Undefined type %s.", strconv.Quote(typ))
|
||||||
}
|
}
|
||||||
if !isValidKind(typDef.Kind, Object) {
|
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 {
|
switch def.Kind {
|
||||||
case Object, Interface:
|
case Object, Interface:
|
||||||
if len(def.Fields) == 0 {
|
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 {
|
for _, field := range def.Fields {
|
||||||
if typ, ok := schema.Types[field.Type.Name()]; ok {
|
if typ, ok := schema.Types[field.Type.Name()]; ok {
|
||||||
if !isValidKind(typ.Kind, Scalar, Object, Interface, Union, Enum) {
|
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:
|
case Enum:
|
||||||
if len(def.EnumValues) == 0 {
|
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 _, value := range def.EnumValues {
|
||||||
for _, nonEnum := range [3]string{"true", "false", "null"} {
|
for _, nonEnum := range [3]string{"true", "false", "null"} {
|
||||||
if value.Name == nonEnum {
|
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
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case InputObject:
|
case InputObject:
|
||||||
if len(def.Fields) == 0 {
|
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 {
|
for _, field := range def.Fields {
|
||||||
if typ, ok := schema.Types[field.Type.Name()]; ok {
|
if typ, ok := schema.Types[field.Type.Name()]; ok {
|
||||||
if !isValidKind(typ.Kind, Scalar, Enum, InputObject) {
|
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 idx, field1 := range def.Fields {
|
||||||
for _, field2 := range def.Fields[idx+1:] {
|
for _, field2 := range def.Fields[idx+1:] {
|
||||||
if field1.Name == field2.Name {
|
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
|
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 {
|
for _, arg := range args {
|
||||||
if err := validateName(arg.Position, arg.Name); err != nil {
|
if err := validateName(arg.Position, arg.Name); err != nil {
|
||||||
// now, GraphQL spec doesn't have reserved argument name
|
// now, GraphQL spec doesn't have reserved argument name
|
||||||
@@ -353,45 +435,71 @@ func validateArgs(schema *Schema, args ArgumentDefinitionList, currentDirective
|
|||||||
def.Kind,
|
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 err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
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 {
|
for _, dir := range dirs {
|
||||||
if err := validateName(dir.Position, dir.Name); err != nil {
|
if err := validateName(dir.Position, dir.Name); err != nil {
|
||||||
// now, GraphQL spec doesn't have reserved directive name
|
// now, GraphQL spec doesn't have reserved directive name
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if currentDirective != nil && dir.Name == currentDirective.Name {
|
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]
|
dirDefinition := schema.Directives[dir.Name]
|
||||||
if dirDefinition == nil {
|
if dirDefinition == nil {
|
||||||
return gqlerror.ErrorPosf(dir.Position, "Undefined directive %s.", dir.Name)
|
return gqlerror.ErrorPosf(dir.Position, "Undefined directive %s.", dir.Name)
|
||||||
}
|
}
|
||||||
validKind := false
|
validKind := slices.Contains(dirDefinition.Locations, location)
|
||||||
for _, dirLocation := range dirDefinition.Locations {
|
|
||||||
if dirLocation == location {
|
|
||||||
validKind = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !validKind {
|
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 {
|
for _, arg := range dir.Arguments {
|
||||||
if dirDefinition.Arguments.ForName(arg.Name) == nil {
|
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 {
|
for _, schemaArg := range dirDefinition.Arguments {
|
||||||
if schemaArg.Type.NonNull && schemaArg.DefaultValue == nil {
|
if schemaArg.Type.NonNull && schemaArg.DefaultValue == nil {
|
||||||
if arg := dir.Arguments.ForName(schemaArg.Name); arg == nil || arg.Value.Kind == NullValue {
|
if arg := dir.Arguments.ForName(
|
||||||
return gqlerror.ErrorPosf(dir.Position, "Argument %s for directive %s cannot be null.", schemaArg.Name, dir.Name)
|
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))
|
return gqlerror.ErrorPosf(def.Position, "Undefined type %s.", strconv.Quote(intfName))
|
||||||
}
|
}
|
||||||
if intf.Kind != Interface {
|
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 {
|
for _, requiredField := range intf.Fields {
|
||||||
foundField := def.Fields.ForName(requiredField.Name)
|
foundField := def.Fields.ForName(requiredField.Name)
|
||||||
@@ -429,24 +542,37 @@ func validateImplements(schema *Schema, def *Definition, intfName string) *gqler
|
|||||||
for _, requiredArg := range requiredField.Arguments {
|
for _, requiredArg := range requiredField.Arguments {
|
||||||
foundArg := foundField.Arguments.ForName(requiredArg.Name)
|
foundArg := foundField.Arguments.ForName(requiredArg.Name)
|
||||||
if foundArg == nil {
|
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.`,
|
`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) {
|
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.`,
|
`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 {
|
for _, foundArgs := range foundField.Arguments {
|
||||||
if requiredField.Arguments.ForName(foundArgs.Name) == nil && foundArgs.Type.NonNull && foundArgs.DefaultValue == nil {
|
if requiredField.Arguments.ForName(foundArgs.Name) == nil && foundArgs.Type.NonNull &&
|
||||||
return gqlerror.ErrorPosf(foundArgs.Position,
|
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.`,
|
`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
|
// validateTypeImplementsAncestors
|
||||||
// https://github.com/graphql/graphql-js/blob/47bd8c8897c72d3efc17ecb1599a95cee6bac5e8/src/type/validate.ts#L428
|
// 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]
|
intf := schema.Types[intfName]
|
||||||
if intf == nil {
|
if intf == nil {
|
||||||
return gqlerror.ErrorPosf(def.Position, "Undefined type %s.", strconv.Quote(intfName))
|
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 {
|
func containsString(slice []string, want string) bool {
|
||||||
for _, str := range slice {
|
return slices.Contains(slice, want)
|
||||||
if want == str {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func isCovariant(schema *Schema, required *Type, actual *Type) bool {
|
func isCovariant(schema *Schema, required, actual *Type) bool {
|
||||||
if required.NonNull && !actual.NonNull {
|
if required.NonNull && !actual.NonNull {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -513,18 +638,17 @@ func isCovariant(schema *Schema, required *Type, actual *Type) bool {
|
|||||||
|
|
||||||
func validateName(pos *Position, name string) *gqlerror.Error {
|
func validateName(pos *Position, name string) *gqlerror.Error {
|
||||||
if strings.HasPrefix(name, "__") {
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func isValidKind(kind DefinitionKind, valid ...DefinitionKind) bool {
|
func isValidKind(kind DefinitionKind, valid ...DefinitionKind) bool {
|
||||||
for _, k := range valid {
|
return slices.Contains(valid, kind)
|
||||||
if kind == k {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func kindList(kinds ...DefinitionKind) string {
|
func kindList(kinds ...DefinitionKind) string {
|
||||||
|
|||||||
+12
-7
@@ -2,6 +2,7 @@ package validator
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
//nolint:staticcheck // bad, yeah
|
//nolint:staticcheck // bad, yeah
|
||||||
. "github.com/vektah/gqlparser/v2/ast"
|
. "github.com/vektah/gqlparser/v2/ast"
|
||||||
"github.com/vektah/gqlparser/v2/gqlerror"
|
"github.com/vektah/gqlparser/v2/gqlerror"
|
||||||
@@ -24,7 +25,7 @@ var (
|
|||||||
OrList = core.OrList
|
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) {
|
func Walk(schema *Schema, document *QueryDocument, observers *Events) {
|
||||||
core.Walk(schema, document, observers)
|
core.Walk(schema, document, observers)
|
||||||
}
|
}
|
||||||
@@ -49,9 +50,9 @@ func AddRule(name string, ruleFunc RuleFunc) {
|
|||||||
|
|
||||||
// RemoveRule removes an existing rule from the rule set
|
// RemoveRule removes an existing rule from the rule set
|
||||||
// if one of the same name exists.
|
// 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) {
|
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 {
|
for _, r := range specifiedRules {
|
||||||
if r.Name == name {
|
if r.Name == name {
|
||||||
continue
|
continue
|
||||||
@@ -64,10 +65,10 @@ func RemoveRule(name string) {
|
|||||||
// ReplaceRule replaces an existing rule from the rule set
|
// ReplaceRule replaces an existing rule from the rule set
|
||||||
// if one of the same name exists.
|
// if one of the same name exists.
|
||||||
// If no match is found, it will add a new rule to the rule set.
|
// 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) {
|
func ReplaceRule(name string, ruleFunc RuleFunc) {
|
||||||
var found bool
|
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 {
|
for _, r := range specifiedRules {
|
||||||
if r.Name == name {
|
if r.Name == name {
|
||||||
found = true
|
found = true
|
||||||
@@ -117,7 +118,11 @@ func Validate(schema *Schema, doc *QueryDocument, rules ...Rule) gqlerror.List {
|
|||||||
return errs
|
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 {
|
if rules == nil {
|
||||||
rules = validatorrules.NewDefaultRules()
|
rules = validatorrules.NewDefaultRules()
|
||||||
}
|
}
|
||||||
@@ -134,7 +139,7 @@ func ValidateWithRules(schema *Schema, doc *QueryDocument, rules *validatorrules
|
|||||||
}
|
}
|
||||||
observers := &core.Events{}
|
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() {
|
for name, ruleFunc := range rules.GetInner() {
|
||||||
currentRules = append(currentRules, Rule{Name: name, RuleFunc: ruleFunc})
|
currentRules = append(currentRules, Rule{Name: name, RuleFunc: ruleFunc})
|
||||||
// ensure deterministic order evaluation
|
// ensure deterministic order evaluation
|
||||||
|
|||||||
+37
-11
@@ -2,6 +2,7 @@ package validator
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -12,11 +13,15 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
//nolint:staticcheck // We do not care about capitalized error strings
|
//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
|
// VariableValues coerces and validates variable values.
|
||||||
func VariableValues(schema *ast.Schema, op *ast.OperationDefinition, variables map[string]interface{}) (map[string]interface{}, error) {
|
func VariableValues(
|
||||||
coercedVars := map[string]interface{}{}
|
schema *ast.Schema,
|
||||||
|
op *ast.OperationDefinition,
|
||||||
|
variables map[string]any,
|
||||||
|
) (map[string]any, error) {
|
||||||
|
coercedVars := map[string]any{}
|
||||||
|
|
||||||
validator := varValidator{
|
validator := varValidator{
|
||||||
path: ast.Path{ast.PathName("variable")},
|
path: ast.Path{ast.PathName("variable")},
|
||||||
@@ -60,13 +65,23 @@ func VariableValues(schema *ast.Schema, op *ast.OperationDefinition, variables m
|
|||||||
case "Int":
|
case "Int":
|
||||||
n, err := jsonNumber.Int64()
|
n, err := jsonNumber.Int64()
|
||||||
if err != nil {
|
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)
|
rv = reflect.ValueOf(n)
|
||||||
case "Float":
|
case "Float":
|
||||||
f, err := jsonNumber.Float64()
|
f, err := jsonNumber.Float64()
|
||||||
if err != nil {
|
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)
|
rv = reflect.ValueOf(f)
|
||||||
}
|
}
|
||||||
@@ -93,7 +108,10 @@ type varValidator struct {
|
|||||||
schema *ast.Schema
|
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
|
currentPath := v.path
|
||||||
resetPath := func() {
|
resetPath := func() {
|
||||||
v.path = currentPath
|
v.path = currentPath
|
||||||
@@ -137,7 +155,8 @@ func (v *varValidator) validateVarType(typ *ast.Type, val reflect.Value) (reflec
|
|||||||
switch def.Kind {
|
switch def.Kind {
|
||||||
case ast.Enum:
|
case ast.Enum:
|
||||||
kind := val.Type().Kind()
|
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")
|
return val, gqlerror.ErrorPathf(v.path, "enums must be ints or strings")
|
||||||
}
|
}
|
||||||
isValidEnum := false
|
isValidEnum := false
|
||||||
@@ -154,11 +173,17 @@ func (v *varValidator) validateVarType(typ *ast.Type, val reflect.Value) (reflec
|
|||||||
kind := val.Type().Kind()
|
kind := val.Type().Kind()
|
||||||
switch typ.NamedType {
|
switch typ.NamedType {
|
||||||
case "Int":
|
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
|
return val, nil
|
||||||
}
|
}
|
||||||
case "Float":
|
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
|
return val, nil
|
||||||
}
|
}
|
||||||
case "String":
|
case "String":
|
||||||
@@ -172,7 +197,8 @@ func (v *varValidator) validateVarType(typ *ast.Type, val reflect.Value) (reflec
|
|||||||
}
|
}
|
||||||
|
|
||||||
case "ID":
|
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
|
return val, nil
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1132,7 +1132,7 @@ github.com/transparency-dev/merkle/rfc6962
|
|||||||
## explicit; go 1.12
|
## explicit; go 1.12
|
||||||
github.com/valyala/fastjson
|
github.com/valyala/fastjson
|
||||||
github.com/valyala/fastjson/fastfloat
|
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
|
## explicit; go 1.22
|
||||||
github.com/vektah/gqlparser/v2/ast
|
github.com/vektah/gqlparser/v2/ast
|
||||||
github.com/vektah/gqlparser/v2/gqlerror
|
github.com/vektah/gqlparser/v2/gqlerror
|
||||||
|
|||||||
Reference in New Issue
Block a user