From 53a78091218a2799c3774d92ffd0782bb4b77afe Mon Sep 17 00:00:00 2001 From: Tim Vergenz Date: Sun, 19 Nov 2017 00:24:47 -0800 Subject: [PATCH 1/4] Split internal/common/values into input_values, arguments --- internal/common/{values.go => arguments.go} | 39 -------------------- internal/common/input_values.go | 40 +++++++++++++++++++++ 2 files changed, 40 insertions(+), 39 deletions(-) rename internal/common/{values.go => arguments.go} (50%) create mode 100644 internal/common/input_values.go diff --git a/internal/common/values.go b/internal/common/arguments.go similarity index 50% rename from internal/common/values.go rename to internal/common/arguments.go index 794f68de..309ae7b4 100644 --- a/internal/common/values.go +++ b/internal/common/arguments.go @@ -1,44 +1,5 @@ package common -import ( - "github.com/neelance/graphql-go/errors" -) - -type InputValue struct { - Name Ident - Type Type - Default Literal - Desc string - Loc errors.Location - TypeLoc errors.Location -} - -type InputValueList []*InputValue - -func (l InputValueList) Get(name string) *InputValue { - for _, v := range l { - if v.Name.Name == name { - return v - } - } - return nil -} - -func ParseInputValue(l *Lexer) *InputValue { - p := &InputValue{} - p.Loc = l.Location() - p.Desc = l.DescComment() - p.Name = l.ConsumeIdentWithLoc() - l.ConsumeToken(':') - p.TypeLoc = l.Location() - p.Type = ParseType(l) - if l.Peek() == '=' { - l.ConsumeToken('=') - p.Default = ParseLiteral(l, true) - } - return p -} - type Argument struct { Name Ident Value Literal diff --git a/internal/common/input_values.go b/internal/common/input_values.go new file mode 100644 index 00000000..f9ea8973 --- /dev/null +++ b/internal/common/input_values.go @@ -0,0 +1,40 @@ +package common + +import ( + "github.com/neelance/graphql-go/errors" +) + +type InputValue struct { + Name Ident + Type Type + Default Literal + Desc string + Loc errors.Location + TypeLoc errors.Location +} + +type InputValueList []*InputValue + +func (l InputValueList) Get(name string) *InputValue { + for _, v := range l { + if v.Name.Name == name { + return v + } + } + return nil +} + +func ParseInputValue(l *Lexer) *InputValue { + p := &InputValue{} + p.Loc = l.Location() + p.Desc = l.DescComment() + p.Name = l.ConsumeIdentWithLoc() + l.ConsumeToken(':') + p.TypeLoc = l.Location() + p.Type = ParseType(l) + if l.Peek() == '=' { + l.ConsumeToken('=') + p.Default = ParseLiteral(l, true) + } + return p +} From bdfba87d7e70bfb4043e5021742b197e96e2f60c Mon Sep 17 00:00:00 2001 From: Tim Vergenz Date: Sun, 19 Nov 2017 00:32:48 -0800 Subject: [PATCH 2/4] Consolidate InputValueList parsing functions --- internal/common/input_values.go | 22 ++++++++++++++++++++++ internal/schema/schema.go | 23 +++-------------------- 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/internal/common/input_values.go b/internal/common/input_values.go index f9ea8973..93b23822 100644 --- a/internal/common/input_values.go +++ b/internal/common/input_values.go @@ -38,3 +38,25 @@ func ParseInputValue(l *Lexer) *InputValue { } return p } + +func ParseArgumentDeclList(l *Lexer) InputValueList { + var args InputValueList + if l.Peek() == '(' { + l.ConsumeToken('(') + for l.Peek() != ')' { + args = append(args, ParseInputValue(l)) + } + l.ConsumeToken(')') + } + return args +} + +func ParseInputFieldList(typeName string, l *Lexer) InputValueList { + l.ConsumeToken('{') + var list InputValueList + for l.Peek() != '}' { + list = append(list, ParseInputValue(l)) + } + l.ConsumeToken('}') + return list +} diff --git a/internal/schema/schema.go b/internal/schema/schema.go index 0cada3a9..153f1004 100644 --- a/internal/schema/schema.go +++ b/internal/schema/schema.go @@ -393,11 +393,7 @@ func parseUnionDecl(l *common.Lexer) *Union { func parseInputDecl(l *common.Lexer) *InputObject { i := &InputObject{} i.Name = l.ConsumeIdent() - l.ConsumeToken('{') - for l.Peek() != '}' { - i.Values = append(i.Values, common.ParseInputValue(l)) - } - l.ConsumeToken('}') + i.Values = common.ParseInputFieldList(i.Name, l) return i } @@ -420,14 +416,7 @@ func parseDirectiveDecl(l *common.Lexer) *DirectiveDecl { d := &DirectiveDecl{} l.ConsumeToken('@') d.Name = l.ConsumeIdent() - if l.Peek() == '(' { - l.ConsumeToken('(') - for l.Peek() != ')' { - v := common.ParseInputValue(l) - d.Args = append(d.Args, v) - } - l.ConsumeToken(')') - } + d.Args = common.ParseArgumentDeclList(l) l.ConsumeKeyword("on") for { loc := l.ConsumeIdent() @@ -446,13 +435,7 @@ func parseFields(l *common.Lexer) FieldList { f := &Field{} f.Desc = l.DescComment() f.Name = l.ConsumeIdent() - if l.Peek() == '(' { - l.ConsumeToken('(') - for l.Peek() != ')' { - f.Args = append(f.Args, common.ParseInputValue(l)) - } - l.ConsumeToken(')') - } + f.Args = common.ParseArgumentDeclList(l) l.ConsumeToken(':') f.Type = common.ParseType(l) f.Directives = common.ParseDirectives(l) From c29f6ff5c77fc8554480d6910e393751b3bf888d Mon Sep 17 00:00:00 2001 From: Tim Vergenz Date: Sun, 19 Nov 2017 00:37:23 -0800 Subject: [PATCH 3/4] Clean up parseField token consumption --- internal/schema/schema.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/internal/schema/schema.go b/internal/schema/schema.go index 153f1004..195462de 100644 --- a/internal/schema/schema.go +++ b/internal/schema/schema.go @@ -363,18 +363,14 @@ func parseObjectDecl(l *common.Lexer) *Object { } } } - l.ConsumeToken('{') o.Fields = parseFields(l) - l.ConsumeToken('}') return o } func parseInterfaceDecl(l *common.Lexer) *Interface { i := &Interface{} i.Name = l.ConsumeIdent() - l.ConsumeToken('{') i.Fields = parseFields(l) - l.ConsumeToken('}') return i } @@ -431,6 +427,7 @@ func parseDirectiveDecl(l *common.Lexer) *DirectiveDecl { func parseFields(l *common.Lexer) FieldList { var fields FieldList + l.ConsumeToken('{') for l.Peek() != '}' { f := &Field{} f.Desc = l.DescComment() @@ -441,5 +438,6 @@ func parseFields(l *common.Lexer) FieldList { f.Directives = common.ParseDirectives(l) fields = append(fields, f) } + l.ConsumeToken('}') return fields } From d19a93080d359cb4274deb2780449e48c8169088 Mon Sep 17 00:00:00 2001 From: Tim Vergenz Date: Sun, 19 Nov 2017 00:19:00 -0800 Subject: [PATCH 4/4] Fail to parse empty field lists Per the spec, [Object][1], [Interface][2], and [Input Object][3] types must define at least one field. This commit makes graphql-go fail to parse any of these types if no fields are defined. [1]: http://facebook.github.io/graphql/October2016/#sec-Object-type-validation [2]: http://facebook.github.io/graphql/October2016/#sec-Interface-type-validation [3]: http://facebook.github.io/graphql/October2016/#sec-Input-Object-type-validation --- graphql_test.go | 5 +++++ internal/common/input_values.go | 5 +++++ internal/schema/schema.go | 9 ++++++--- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/graphql_test.go b/graphql_test.go index 8e581afb..b2b07825 100644 --- a/graphql_test.go +++ b/graphql_test.go @@ -581,6 +581,10 @@ func TestIncludeDirective(t *testing.T) { type testDeprecatedDirectiveResolver struct{} +func (r *testDeprecatedDirectiveResolver) Test() string { + return "" +} + func (r *testDeprecatedDirectiveResolver) A() int32 { return 0 } @@ -643,6 +647,7 @@ func TestDeprecatedDirective(t *testing.T) { } type Query { + test: String! } enum Test { diff --git a/internal/common/input_values.go b/internal/common/input_values.go index 93b23822..5d70f28e 100644 --- a/internal/common/input_values.go +++ b/internal/common/input_values.go @@ -1,6 +1,8 @@ package common import ( + "fmt" + "github.com/neelance/graphql-go/errors" ) @@ -57,6 +59,9 @@ func ParseInputFieldList(typeName string, l *Lexer) InputValueList { for l.Peek() != '}' { list = append(list, ParseInputValue(l)) } + if len(list) == 0 { + l.SyntaxError(fmt.Sprintf(`input type %q must define one or more fields`, typeName)) + } l.ConsumeToken('}') return list } diff --git a/internal/schema/schema.go b/internal/schema/schema.go index 195462de..43d38b84 100644 --- a/internal/schema/schema.go +++ b/internal/schema/schema.go @@ -363,14 +363,14 @@ func parseObjectDecl(l *common.Lexer) *Object { } } } - o.Fields = parseFields(l) + o.Fields = parseFields("object", o.Name, l) return o } func parseInterfaceDecl(l *common.Lexer) *Interface { i := &Interface{} i.Name = l.ConsumeIdent() - i.Fields = parseFields(l) + i.Fields = parseFields("interface", i.Name, l) return i } @@ -425,7 +425,7 @@ func parseDirectiveDecl(l *common.Lexer) *DirectiveDecl { return d } -func parseFields(l *common.Lexer) FieldList { +func parseFields(declType, typeName string, l *common.Lexer) FieldList { var fields FieldList l.ConsumeToken('{') for l.Peek() != '}' { @@ -438,6 +438,9 @@ func parseFields(l *common.Lexer) FieldList { f.Directives = common.ParseDirectives(l) fields = append(fields, f) } + if len(fields) == 0 { + l.SyntaxError(fmt.Sprintf(`%s type %q must define one or more fields`, declType, typeName)) + } l.ConsumeToken('}') return fields }