Skip to content

Commit

Permalink
add support for repeatable directives (#502)
Browse files Browse the repository at this point in the history
add support for repeatable directives
  • Loading branch information
speezepearson authored Mar 20, 2022
1 parent 426e470 commit 0140894
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 6 deletions.
35 changes: 34 additions & 1 deletion internal/schema/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -513,10 +513,21 @@ func parseDirectiveDef(l *common.Lexer) *types.DirectiveDefinition {
l.ConsumeToken(')')
}

l.ConsumeKeyword("on")
switch x := l.ConsumeIdent(); x {
case "on":
// no-op; Go doesn't fallthrough by default
case "repeatable":
d.Repeatable = true
l.ConsumeKeyword("on")
default:
l.SyntaxError(fmt.Sprintf(`unexpected %q, expecting "on" or "repeatable"`, x))
}

for {
loc := l.ConsumeIdent()
if _, ok := legalDirectiveLocationNames[loc]; !ok {
l.SyntaxError(fmt.Sprintf("%q is not a legal directive location (options: %v)", loc, legalDirectiveLocationNames))
}
d.Locations = append(d.Locations, loc)
if l.Peek() != '|' {
break
Expand Down Expand Up @@ -586,3 +597,25 @@ func parseFieldsDef(l *common.Lexer) types.FieldsDefinition {
}
return fields
}

var legalDirectiveLocationNames = map[string]struct{}{
"SCHEMA": {},
"SCALAR": {},
"OBJECT": {},
"FIELD_DEFINITION": {},
"ARGUMENT_DEFINITION": {},
"INTERFACE": {},
"UNION": {},
"ENUM": {},
"ENUM_VALUE": {},
"INPUT_OBJECT": {},
"INPUT_FIELD_DEFINITION": {},
"QUERY": {},
"MUTATION": {},
"SUBSCRIPTION": {},
"FIELD": {},
"FRAGMENT_DEFINITION": {},
"FRAGMENT_SPREAD": {},
"INLINE_FRAGMENT": {},
"VARIABLE_DEFINITION": {},
}
53 changes: 53 additions & 0 deletions internal/schema/schema_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package schema_test

import (
"fmt"
"strings"
"testing"

"github.com/graph-gophers/graphql-go/internal/schema"
Expand Down Expand Up @@ -813,6 +814,7 @@ Second line of the description.
| ENUM_VALUE
| INPUT_OBJECT
| INPUT_FIELD_DEFINITION
directive @repeatabledirective repeatable on SCALAR
interface NamedEntity @directive { name: String }
Expand All @@ -834,6 +836,8 @@ Second line of the description.
}
union Union @uniondirective = Photo | Person
scalar Mass @repeatabledirective @repeatabledirective
`,
validateSchema: func(s *types.Schema) error {
namedEntityDirectives := s.Types["NamedEntity"].(*types.InterfaceTypeDefinition).Directives
Expand Down Expand Up @@ -864,6 +868,55 @@ Second line of the description.
if len(unionDirectives) != 1 || unionDirectives[0].Name.Name != "uniondirective" {
return fmt.Errorf("missing directive on Union union, expected @uniondirective but got %v", unionDirectives)
}

massDirectives := s.Types["Mass"].(*types.ScalarTypeDefinition).Directives
if len(massDirectives) != 2 || massDirectives[0].Name.Name != "repeatabledirective" || massDirectives[1].Name.Name != "repeatabledirective" {
return fmt.Errorf("missing directive on Repeatable scalar, expected @repeatabledirective @repeatabledirective but got %v", massDirectives)
}
return nil
},
},
{
name: "Sets Directive.Repeatable if `repeatable` keyword is given",
sdl: `
directive @nonrepeatabledirective on SCALAR
directive @repeatabledirective repeatable on SCALAR
`,
validateSchema: func(s *types.Schema) error {
if dir := s.Directives["nonrepeatabledirective"]; dir.Repeatable {
return fmt.Errorf("did not expect directive to be repeatable: %v", dir)
}
if dir := s.Directives["repeatabledirective"]; !dir.Repeatable {
return fmt.Errorf("expected directive to be repeatable: %v", dir)
}
return nil
},
},
{
name: "Directive definition does not allow double-`repeatable`",
sdl: `
directive @mydirective repeatable repeatable SCALAR
scalar MyScalar @mydirective
`,
validateError: func(err error) error {
msg := `graphql: syntax error: unexpected "repeatable", expecting "on" (line 2, column 38)`
if err == nil || err.Error() != msg {
return fmt.Errorf("expected error %q, but got %q", msg, err)
}
return nil
},
},
{
name: "Directive definition does not allow double-`on` instead of `repeatable on`",
sdl: `
directive @mydirective on on SCALAR
scalar MyScalar @mydirective
`,
validateError: func(err error) error {
prefix := `graphql: syntax error: "on" is not a legal directive location`
if err == nil || !strings.HasPrefix(err.Error(), prefix) {
return fmt.Errorf("expected error starting with %q, but got %q", prefix, err)
}
return nil
},
},
Expand Down
11 changes: 6 additions & 5 deletions types/directive.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@ type Directive struct {
//
// http://spec.graphql.org/draft/#sec-Type-System.Directives
type DirectiveDefinition struct {
Name string
Desc string
Locations []string
Arguments ArgumentsDefinition
Loc errors.Location
Name string
Desc string
Repeatable bool
Locations []string
Arguments ArgumentsDefinition
Loc errors.Location
}

type DirectiveList []*Directive
Expand Down

0 comments on commit 0140894

Please sign in to comment.