Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 4 additions & 20 deletions validator/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,15 @@ import (
"github.com/vektah/gqlparser/errors"
)

func Error(options ...Option) errors.Validation {
var err errors.Validation
type ErrorOption func(err *errors.Validation)

for _, o := range options {
o(&err)
}

return err
}

type Option func(err *errors.Validation)

func Rule(rule string) Option {
return func(err *errors.Validation) {
err.Rule = rule
}
}

func Message(msg string, args ...interface{}) Option {
func Message(msg string, args ...interface{}) ErrorOption {
return func(err *errors.Validation) {
err.Message = fmt.Sprintf(msg, args...)
}
}

func SuggestList(typed string, suggestions []string) Option {
func SuggestList(typed string, suggestions []string) ErrorOption {
suggested := suggestionList(typed, suggestions)
return func(err *errors.Validation) {
if len(suggested) > 0 {
Expand All @@ -39,7 +23,7 @@ func SuggestList(typed string, suggestions []string) Option {
}
}

func Suggestf(suggestion string, args ...interface{}) Option {
func Suggestf(suggestion string, args ...interface{}) ErrorOption {
return func(err *errors.Validation) {
err.Message += " Did you mean " + fmt.Sprintf(suggestion, args...) + "?"
}
Expand Down
43 changes: 20 additions & 23 deletions validator/fields_on_correct_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,40 +5,37 @@ import (
"sort"

"github.com/vektah/gqlparser"
"github.com/vektah/gqlparser/errors"
)

func init() {
fieldVisitors = append(fieldVisitors, fieldsOnCorrectType)
}
addRule("FieldsOnCorrectType", func(observers *Events, addError addErrFunc) {
observers.OnField(func(walker *Walker, parentDef *gqlparser.Definition, fieldDef *gqlparser.FieldDefinition, field *gqlparser.Field) {
if parentDef == nil {
return
}

func fieldsOnCorrectType(ctx *vctx, parentDef *gqlparser.Definition, fieldDef *gqlparser.FieldDefinition, field *gqlparser.Field) {
if parentDef == nil {
return
}
if fieldDef != nil {
return
}

if fieldDef != nil {
return
}
message := fmt.Sprintf(`Cannot query field "%s" on type "%s".`, field.Name, parentDef.Name)

message := fmt.Sprintf(`Cannot query field "%s" on type "%s".`, field.Name, parentDef.Name)
if suggestedTypeNames := getSuggestedTypeNames(walker, parentDef, field.Name); suggestedTypeNames != nil {
message += " Did you mean to use an inline fragment on " + quotedOrList(suggestedTypeNames...) + "?"
} else if suggestedFieldNames := getSuggestedFieldNames(parentDef, field.Name); suggestedFieldNames != nil {
message += " Did you mean " + quotedOrList(suggestedFieldNames...) + "?"
}

if suggestedTypeNames := getSuggestedTypeNames(ctx, parentDef, field.Name); suggestedTypeNames != nil {
message += " Did you mean to use an inline fragment on " + quotedOrList(suggestedTypeNames...) + "?"
} else if suggestedFieldNames := getSuggestedFieldNames(ctx, parentDef, field.Name); suggestedFieldNames != nil {
message += " Did you mean " + quotedOrList(suggestedFieldNames...) + "?"
}
ctx.errors = append(ctx.errors, errors.Validation{
Message: message,
Rule: "FieldsOnCorrectType",
addError(Message(message))
})
})
}

// Go through all of the implementations of type, as well as the interfaces
// that they implement. If any of those types include the provided field,
// suggest them, sorted by how often the type is referenced, starting
// with Interfaces.
func getSuggestedTypeNames(ctx *vctx, parent *gqlparser.Definition, name string) []string {
func getSuggestedTypeNames(walker *Walker, parent *gqlparser.Definition, name string) []string {
if !parent.IsAbstractType() {
return nil
}
Expand All @@ -47,7 +44,7 @@ func getSuggestedTypeNames(ctx *vctx, parent *gqlparser.Definition, name string)
var suggestedInterfaceTypes []string
interfaceUsageCount := map[string]int{}

for _, possibleType := range ctx.schema.GetPossibleTypes(parent) {
for _, possibleType := range walker.Schema.GetPossibleTypes(parent) {
field := possibleType.Field(name)
if field == nil {
continue
Expand All @@ -56,7 +53,7 @@ func getSuggestedTypeNames(ctx *vctx, parent *gqlparser.Definition, name string)
suggestedObjectTypes = append(suggestedObjectTypes, possibleType.Name)

for _, possibleInterface := range possibleType.Interfaces {
interfaceField := ctx.schema.Types[possibleInterface.Name()]
interfaceField := walker.Schema.Types[possibleInterface.Name()]
if interfaceField != nil && interfaceField.Field(name) != nil {
if interfaceUsageCount[possibleInterface.Name()] == 0 {
suggestedInterfaceTypes = append(suggestedInterfaceTypes, possibleInterface.Name())
Expand All @@ -75,7 +72,7 @@ func getSuggestedTypeNames(ctx *vctx, parent *gqlparser.Definition, name string)

// For the field name provided, determine if there are any similar field names
// that may be the result of a typo.
func getSuggestedFieldNames(ctx *vctx, parent *gqlparser.Definition, name string) []string {
func getSuggestedFieldNames(parent *gqlparser.Definition, name string) []string {
if parent.Kind != gqlparser.Object && parent.Kind != gqlparser.Interface {
return nil
}
Expand Down
56 changes: 24 additions & 32 deletions validator/fragments_on_composite_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,47 +4,39 @@ import (
"fmt"

"github.com/vektah/gqlparser"
"github.com/vektah/gqlparser/errors"
)

func init() {
inlineFragmentVisitors = append(inlineFragmentVisitors, inlineFragmentOnCompositeTypes)
fragmentVisitors = append(fragmentVisitors, fragmentOnCompositeTypes)
}

func inlineFragmentOnCompositeTypes(ctx *vctx, parentDef *gqlparser.Definition, inlineFragment *gqlparser.InlineFragment) {
if parentDef == nil {
return
}
addRule("FragmentsOnCompositeTypes", func(observers *Events, addError addErrFunc) {
observers.OnInlineFragment(func(walker *Walker, parentDef *gqlparser.Definition, inlineFragment *gqlparser.InlineFragment) {
if parentDef == nil {
return
}

fragmentType := ctx.schema.Types[inlineFragment.TypeCondition.Name()]
if fragmentType == nil || fragmentType.IsCompositeType() {
return
}
fragmentType := walker.Schema.Types[inlineFragment.TypeCondition.Name()]
if fragmentType == nil || fragmentType.IsCompositeType() {
return
}

message := fmt.Sprintf(`Fragment cannot condition on non composite type "%s".`, inlineFragment.TypeCondition.Name())
message := fmt.Sprintf(`Fragment cannot condition on non composite type "%s".`, inlineFragment.TypeCondition.Name())

ctx.errors = append(ctx.errors, errors.Validation{
Message: message,
Rule: "FragmentsOnCompositeTypes",
})
}
addError(Message(message))
})

func fragmentOnCompositeTypes(ctx *vctx, parentDef *gqlparser.Definition, fragment *gqlparser.FragmentDefinition) {
if parentDef == nil {
return
}
observers.OnFragment(func(walker *Walker, parentDef *gqlparser.Definition, fragment *gqlparser.FragmentDefinition) {
if parentDef == nil {
return
}

if fragment.TypeCondition.Name() == "" {
return
} else if parentDef != nil && parentDef.IsCompositeType() {
return
}
if fragment.TypeCondition.Name() == "" {
return
} else if parentDef != nil && parentDef.IsCompositeType() {
return
}

message := fmt.Sprintf(`Fragment "%s" cannot condition on non composite type "%s".`, fragment.Name, fragment.TypeCondition.Name())
message := fmt.Sprintf(`Fragment "%s" cannot condition on non composite type "%s".`, fragment.Name, fragment.TypeCondition.Name())

ctx.errors = append(ctx.errors, errors.Validation{
Message: message,
Rule: "FragmentsOnCompositeTypes",
addError(Message(message))
})
})
}
95 changes: 46 additions & 49 deletions validator/known_argument_names.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,53 +5,50 @@ import (
)

func init() {
fieldVisitors = append(fieldVisitors, knownFieldArgumentNames)
directiveVisitors = append(directiveVisitors, knownDirectiveArgumentNames)
}

// A GraphQL field is only valid if all supplied arguments are defined by that field.
func knownFieldArgumentNames(ctx *vctx, parentDef *gqlparser.Definition, fieldDef *gqlparser.FieldDefinition, field *gqlparser.Field) {
if fieldDef == nil {
return
}
for _, arg := range field.Arguments {
def := fieldDef.Arguments.ForName(arg.Name)
if def != nil {
continue
}

var suggestions []string
for _, argDef := range fieldDef.Arguments {
suggestions = append(suggestions, argDef.Name)
}

ctx.errors = append(ctx.errors, Error(
Rule("KnownArgumentNames"),
Message(`Unknown argument "%s" on field "%s" of type "%s".`, arg.Name, field.Name, parentDef.Name),
SuggestList(arg.Name, suggestions),
))
}
}

func knownDirectiveArgumentNames(ctx *vctx, parentDef *gqlparser.Definition, directiveDef *gqlparser.DirectiveDefinition, directive *gqlparser.Directive, location gqlparser.DirectiveLocation) {
if directiveDef == nil {
return
}
for _, arg := range directive.Arguments {
def := directiveDef.Arguments.ForName(arg.Name)
if def != nil {
continue
}

var suggestions []string
for _, argDef := range directiveDef.Arguments {
suggestions = append(suggestions, argDef.Name)
}

ctx.errors = append(ctx.errors, Error(
Rule("KnownArgumentNames"),
Message(`Unknown argument "%s" on directive "@%s".`, arg.Name, directive.Name),
SuggestList(arg.Name, suggestions),
))
}
addRule("KnownArgumentNames", func(observers *Events, addError addErrFunc) {
// A GraphQL field is only valid if all supplied arguments are defined by that field.
observers.OnField(func(walker *Walker, parentDef *gqlparser.Definition, fieldDef *gqlparser.FieldDefinition, field *gqlparser.Field) {
if fieldDef == nil {
return
}
for _, arg := range field.Arguments {
def := fieldDef.Arguments.ForName(arg.Name)
if def != nil {
continue
}

var suggestions []string
for _, argDef := range fieldDef.Arguments {
suggestions = append(suggestions, argDef.Name)
}

addError(
Message(`Unknown argument "%s" on field "%s" of type "%s".`, arg.Name, field.Name, parentDef.Name),
SuggestList(arg.Name, suggestions),
)
}
})

observers.OnDirective(func(walker *Walker, parentDef *gqlparser.Definition, directiveDef *gqlparser.DirectiveDefinition, directive *gqlparser.Directive, location gqlparser.DirectiveLocation) {
if directiveDef == nil {
return
}
for _, arg := range directive.Arguments {
def := directiveDef.Arguments.ForName(arg.Name)
if def != nil {
continue
}

var suggestions []string
for _, argDef := range directiveDef.Arguments {
suggestions = append(suggestions, argDef.Name)
}

addError(
Message(`Unknown argument "%s" on directive "@%s".`, arg.Name, directive.Name),
SuggestList(arg.Name, suggestions),
)
}
})
})
}
42 changes: 21 additions & 21 deletions validator/known_directives.go
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
package validator

import "github.com/vektah/gqlparser"
import (
"github.com/vektah/gqlparser"
)

func init() {
directiveVisitors = append(directiveVisitors, knownDirectives)
}

func knownDirectives(ctx *vctx, parentDef *gqlparser.Definition, directiveDef *gqlparser.DirectiveDefinition, directive *gqlparser.Directive, location gqlparser.DirectiveLocation) {
if directiveDef == nil {
ctx.errors = append(ctx.errors, Error(
Rule("KnownDirectives"),
Message(`Unknown directive "%s".`, directive.Name),
))
return
}
addRule("KnownDirectives", func(observers *Events, addError addErrFunc) {
observers.OnDirective(func(walker *Walker, parentDef *gqlparser.Definition, directiveDef *gqlparser.DirectiveDefinition, directive *gqlparser.Directive, location gqlparser.DirectiveLocation) {
if directiveDef == nil {
addError(
Message(`Unknown directive "%s".`, directive.Name),
)
return
}

for _, loc := range directiveDef.Locations {
if loc == location {
return
}
}
for _, loc := range directiveDef.Locations {
if loc == location {
return
}
}

ctx.errors = append(ctx.errors, Error(
Rule("KnownDirectives"),
Message(`Directive "%s" may not be used on %s.`, directive.Name, location),
))
addError(
Message(`Directive "%s" may not be used on %s.`, directive.Name, location),
)
})
})
}
Loading