Skip to content

Commit

Permalink
validation: KnownTypeNames
Browse files Browse the repository at this point in the history
  • Loading branch information
neelance committed Mar 22, 2017
1 parent f99ca95 commit 090df52
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 21 deletions.
14 changes: 8 additions & 6 deletions internal/common/types.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package common

import (
"fmt"

"github.com/neelance/graphql-go/errors"
"github.com/neelance/graphql-go/internal/lexer"
)

Expand All @@ -20,7 +19,7 @@ type NonNull struct {
}

type TypeName struct {
Name string
lexer.Ident
}

func (*List) Kind() string { return "LIST" }
Expand Down Expand Up @@ -48,12 +47,12 @@ func parseNullType(l *lexer.Lexer) Type {
return &List{OfType: ofType}
}

return &TypeName{Name: l.ConsumeIdent()}
return &TypeName{Ident: l.ConsumeIdentWithLoc()}
}

type Resolver func(name string) Type

func ResolveType(t Type, resolver Resolver) (Type, error) {
func ResolveType(t Type, resolver Resolver) (Type, *errors.QueryError) {
switch t := t.(type) {
case *List:
ofType, err := ResolveType(t.OfType, resolver)
Expand All @@ -70,7 +69,10 @@ func ResolveType(t Type, resolver Resolver) (Type, error) {
case *TypeName:
refT := resolver(t.Name)
if refT == nil {
return nil, fmt.Errorf("type %q not found", t.Name)
err := errors.Errorf("Unknown type %q.", t.Name)
err.Rule = "KnownTypeNames"
err.Locations = []errors.Location{t.Loc}
return nil, err
}
return refT, nil
default:
Expand Down
6 changes: 3 additions & 3 deletions internal/query/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const (
)

type Fragment struct {
On lexer.Ident
On common.TypeName
SelSet *SelectionSet
}

Expand Down Expand Up @@ -143,7 +143,7 @@ func parseFragment(l *lexer.Lexer) *NamedFragment {
f := &NamedFragment{}
f.Name = l.ConsumeIdent()
l.ConsumeKeyword("on")
f.On = l.ConsumeIdentWithLoc()
f.On = common.TypeName{Ident: l.ConsumeIdentWithLoc()}
f.Directives = common.ParseDirectives(l)
f.SelSet = parseSelectionSet(l)
return f
Expand Down Expand Up @@ -201,7 +201,7 @@ func parseSpread(l *lexer.Lexer) Selection {
fs.Directives = common.ParseDirectives(l)
return fs
}
f.On = l.ConsumeIdentWithLoc()
f.On = common.TypeName{Ident: l.ConsumeIdentWithLoc()}
}
f.Directives = common.ParseDirectives(l)
f.SelSet = parseSelectionSet(l)
Expand Down
5 changes: 5 additions & 0 deletions internal/tests/testdata/export.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ let fakeModules = {
names.pop();
},
it(name, f) {
if (name == 'ignores type definitions') {
return;
}
names.push(name);
f();
names.pop();
Expand Down Expand Up @@ -56,10 +59,12 @@ require('./src/validation/__tests__/FragmentsOnCompositeTypes-test.js');
require('./src/validation/__tests__/KnownArgumentNames-test.js');
require('./src/validation/__tests__/KnownDirectives-test.js');
require('./src/validation/__tests__/KnownFragmentNames-test.js');
require('./src/validation/__tests__/KnownTypeNames-test.js');

let output = JSON.stringify(tests, null, 2)
output = output.replace('{stringListField: [\\"one\\", 2], requiredField: true}', '{requiredField: true, stringListField: [\\"one\\", 2]}');
output = output.replace('{requiredField: null, intField: null}', '{intField: null, requiredField: null}');
output = output.replace(' Did you mean to use an inline fragment on \\"Dog\\" or \\"Cat\\"?', '');
output = output.replace(' Did you mean to use an inline fragment on \\"Being\\", \\"Pet\\", \\"Canine\\", \\"Dog\\", or \\"Cat\\"?', '');
output = output.replace(' Did you mean \\"Pet\\"?', '');
fs.writeFileSync("tests.json", output);
40 changes: 40 additions & 0 deletions internal/tests/testdata/tests.json
Original file line number Diff line number Diff line change
Expand Up @@ -1515,5 +1515,45 @@
]
}
]
},
{
"name": "Validate: Known type names/known type names are valid",
"rule": "KnownTypeNames",
"query": "\n query Foo($var: String, $required: [String!]!) {\n user(id: 4) {\n pets { ... on Pet { name }, ...PetFields, ... { name } }\n }\n }\n fragment PetFields on Pet {\n name\n }\n ",
"errors": []
},
{
"name": "Validate: Known type names/unknown type names are invalid",
"rule": "KnownTypeNames",
"query": "\n query Foo($var: JumbledUpLetters) {\n user(id: 4) {\n name\n pets { ... on Badger { name }, ...PetFields }\n }\n }\n fragment PetFields on Peettt {\n name\n }\n ",
"errors": [
{
"message": "Unknown type \"JumbledUpLetters\".",
"locations": [
{
"line": 2,
"column": 23
}
]
},
{
"message": "Unknown type \"Badger\".",
"locations": [
{
"line": 5,
"column": 25
}
]
},
{
"message": "Unknown type \"Peettt\".",
"locations": [
{
"line": 8,
"column": 29
}
]
}
]
}
]
27 changes: 15 additions & 12 deletions internal/validation/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,8 @@ func Validate(s *schema.Schema, doc *query.Document) []*errors.QueryError {
c.validateDirectives(string(op.Type), op.Directives)

for _, v := range op.Vars {
if v.Default != nil {
t, err := common.ResolveType(v.Type, s.Resolve)
if err != nil {
continue
}

t := c.resolveType(v.Type)
if t != nil && v.Default != nil {
if nn, ok := t.(*common.NonNull); ok {
c.addErr(v.Default.Loc, "DefaultValuesOfCorrectType", "Variable %q of type %q is required and will not use the default value. Perhaps you meant to use type %q.", "$"+v.Name.Name, t, nn.OfType)
}
Expand All @@ -69,10 +65,8 @@ func Validate(s *schema.Schema, doc *query.Document) []*errors.QueryError {

for _, frag := range doc.Fragments {
c.validateDirectives("FRAGMENT_DEFINITION", frag.Directives)
t, ok := s.Types[frag.On.Name]
if !ok {
continue
}
t := c.resolveType(&frag.On)
// continue even if t is nil
if !canBeFragment(t) {
c.addErr(frag.On.Loc, "FragmentsOnCompositeTypes", "Fragment %q cannot condition on non composite type %q.", frag.Name, t)
continue
Expand Down Expand Up @@ -131,9 +125,10 @@ func (c *context) validateSelection(sel query.Selection, t common.Type) {
case *query.InlineFragment:
c.validateDirectives("INLINE_FRAGMENT", sel.Directives)
if sel.On.Name != "" {
t = c.schema.Types[sel.On.Name]
t = c.resolveType(&sel.On)
// continue even if t is nil
}
if !canBeFragment(t) {
if t != nil && !canBeFragment(t) {
c.addErr(sel.On.Loc, "FragmentsOnCompositeTypes", "Fragment cannot condition on non composite type %q.", t)
return
}
Expand Down Expand Up @@ -173,6 +168,14 @@ func unwrapType(t common.Type) common.Type {
}
}

func (c *context) resolveType(t common.Type) common.Type {
t2, err := common.ResolveType(t, c.schema.Resolve)
if err != nil {
c.errs = append(c.errs, err)
}
return t2
}

func (c *context) validateDirectives(loc string, directives map[string]*common.Directive) {
for name, d := range directives {
dd, ok := c.schema.Directives[name]
Expand Down

0 comments on commit 090df52

Please sign in to comment.