Skip to content

Commit

Permalink
Separate multiple inherited interfaces with &
Browse files Browse the repository at this point in the history
This replaces:

```graphql
type Foo implements Bar, Baz { field: Type }
```

With:

```graphql
type Foo implements Bar & Baz { field: Type }
```

This is more consistent with other trailing lists of values which
either have an explicit separator (union members) or are prefixed
with a sigil (directives). This avoids parse ambiguity in the
case of an omitted field set, illustrated by
[github.com/graphql/graphql-js#1166](graphql/graphql-js#1166).

For now, the old method of declaration remains valid.

References:
- graphql/graphql-js#1169
- graphql/graphql-spec#90
- graphql/graphql-spec@32b55ed
  • Loading branch information
tonyghita committed Mar 7, 2018
1 parent bdec921 commit 9cbe07c
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 13 deletions.
2 changes: 1 addition & 1 deletion internal/common/lexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ type Ident struct {
Loc errors.Location
}

func New(sc *scanner.Scanner) *Lexer {
func NewLexer(sc *scanner.Scanner) *Lexer {
l := &Lexer{sc: sc}
l.Consume()
return l
Expand Down
2 changes: 1 addition & 1 deletion internal/query/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ func Parse(queryString string) (*Document, *errors.QueryError) {
}
sc.Init(strings.NewReader(queryString))

l := common.New(sc)
l := common.NewLexer(sc)
var doc *Document
err := l.CatchSyntaxError(func() {
doc = parseDocument(l)
Expand Down
27 changes: 16 additions & 11 deletions internal/schema/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,13 +154,14 @@ func New() *Schema {
return s
}

// Parse the schema string.
func (s *Schema) Parse(schemaString string) error {
sc := &scanner.Scanner{
Mode: scanner.ScanIdents | scanner.ScanInts | scanner.ScanFloats | scanner.ScanStrings,
}
sc.Init(strings.NewReader(schemaString))

l := common.New(sc)
l := common.NewLexer(sc)
err := l.CatchSyntaxError(func() {
parseSchema(s, l)
})
Expand Down Expand Up @@ -316,7 +317,7 @@ func parseSchema(s *Schema, l *common.Lexer) {
}
l.ConsumeToken('}')
case "type":
obj := parseObjectDecl(l)
obj := parseObjectDeclaration(l)
obj.Desc = desc
s.Types[obj.Name] = obj
s.objects = append(s.objects, obj)
Expand Down Expand Up @@ -351,22 +352,26 @@ func parseSchema(s *Schema, l *common.Lexer) {
}
}

func parseObjectDecl(l *common.Lexer) *Object {
o := &Object{}
o.Name = l.ConsumeIdent()
func parseObjectDeclaration(l *common.Lexer) *Object {
object := &Object{Name: l.ConsumeIdent()}

if l.Peek() == scanner.Ident {
l.ConsumeKeyword("implements")
for {
o.interfaceNames = append(o.interfaceNames, l.ConsumeIdent())
if l.Peek() == '{' {
break

for l.Peek() != '{' {
object.interfaceNames = append(object.interfaceNames, l.ConsumeIdent())

if l.Peek() == '&' {
l.ConsumeToken('&')
}
}
}

l.ConsumeToken('{')
o.Fields = parseFields(l)
object.Fields = parseFields(l)
l.ConsumeToken('}')
return o

return object
}

func parseInterfaceDecl(l *common.Lexer) *Interface {
Expand Down
71 changes: 71 additions & 0 deletions internal/schema/schema_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package schema

import (
"strings"
"testing"
"text/scanner"

"github.com/graph-gophers/graphql-go/internal/common"
)

type testCase struct {
description string
declaration string
expected *Object
}

func TestParseObjectDeclaration(t *testing.T) {
tests := []testCase{
{
"allows '&' separator",
"Alien implements Being & Intelligent { name: String, iq: Int }",
&Object{
Name: "Alien",
interfaceNames: []string{"Being", "Intelligent"},
},
},
{
"allows legacy ',' separator",
"Alien implements Being, Intelligent { name: String, iq: Int }",
&Object{
Name: "Alien",
interfaceNames: []string{"Being", "Intelligent"},
},
},
}

setup := func(schema string) *common.Lexer {
sc := &scanner.Scanner{
Mode: scanner.ScanIdents | scanner.ScanInts | scanner.ScanFloats | scanner.ScanStrings,
}
sc.Init(strings.NewReader(schema))
return common.NewLexer(sc)
}

for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
lex := setup(test.declaration)
var actual *Object

parse := func() { actual = parseObjectDeclaration(lex) }
if err := lex.CatchSyntaxError(parse); err != nil {
t.Fatal(err)
}

if test.expected.Name != actual.Name {
t.Errorf("wrong object name: want %q, got %q", test.expected.Name, actual.Name)
}

if len(test.expected.interfaceNames) != len(actual.interfaceNames) {
t.Fatalf("wrong number of interface names: want %s, got %s", test.expected.interfaceNames, actual.interfaceNames)
}

for i, expectedName := range test.expected.interfaceNames {
actualName := actual.interfaceNames[i]
if expectedName != actualName {
t.Errorf("wrong interface name: want %q, got %q", expectedName, actualName)
}
}
})
}
}

0 comments on commit 9cbe07c

Please sign in to comment.