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