Skip to content

Commit

Permalink
add types package
Browse files Browse the repository at this point in the history
Part of graph-gophers#434 and related to graph-gophers#116 this change adds a new package containing all
types used by graphql-go in representing the GraphQL specification. The names
used in this package should match the specification as closely as possible.

In order to have cohesion, all internal packages that use GraphQL types have
been changed to use this new package.

This change is large but mostly mechanical. I recommend starting by reading
through the `types` package to build familiarity. I'll call out places in the
code where I made decisions and what the tradeoffs were.
  • Loading branch information
seansorr committed Apr 22, 2021
1 parent 21f7aef commit bf0a0cc
Show file tree
Hide file tree
Showing 38 changed files with 1,271 additions and 1,127 deletions.
11 changes: 6 additions & 5 deletions graphql.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"github.com/graph-gophers/graphql-go/introspection"
"github.com/graph-gophers/graphql-go/log"
"github.com/graph-gophers/graphql-go/trace"
"github.com/graph-gophers/graphql-go/types"
)

// ParseSchema parses a GraphQL schema and attaches the given root resolver. It returns an error if
Expand All @@ -42,7 +43,7 @@ func ParseSchema(schemaString string, resolver interface{}, opts ...SchemaOpt) (
}
}

if err := s.schema.Parse(schemaString, s.useStringDescriptions); err != nil {
if err := schema.Parse(s.schema, schemaString, s.useStringDescriptions); err != nil {
return nil, err
}
if err := s.validateSchema(); err != nil {
Expand All @@ -69,7 +70,7 @@ func MustParseSchema(schemaString string, resolver interface{}, opts ...SchemaOp

// Schema represents a GraphQL schema with an optional resolver.
type Schema struct {
schema *schema.Schema
schema *types.Schema
res *resolvable.Schema

maxDepth int
Expand Down Expand Up @@ -228,7 +229,7 @@ func (s *Schema) exec(ctx context.Context, queryString string, operationName str
}
for _, v := range op.Vars {
if _, ok := variables[v.Name.Name]; !ok && v.Default != nil {
variables[v.Name.Name] = v.Default.Value(nil)
variables[v.Name.Name] = v.Default.Deserialize(nil)
}
}

Expand Down Expand Up @@ -288,7 +289,7 @@ func (t *validationBridgingTracer) TraceValidation(context.Context) trace.TraceV
return t.tracer.TraceValidation()
}

func validateRootOp(s *schema.Schema, name string, mandatory bool) error {
func validateRootOp(s *types.Schema, name string, mandatory bool) error {
t, ok := s.EntryPoints[name]
if !ok {
if mandatory {
Expand All @@ -302,7 +303,7 @@ func validateRootOp(s *schema.Schema, name string, mandatory bool) error {
return nil
}

func getOperation(document *query.Document, operationName string) (*query.Operation, error) {
func getOperation(document *types.ExecutableDefinition, operationName string) (*types.OperationDefinition, error) {
if len(document.Operations) == 0 {
return nil, fmt.Errorf("no operations in query document")
}
Expand Down
24 changes: 5 additions & 19 deletions internal/common/directive.go
Original file line number Diff line number Diff line change
@@ -1,32 +1,18 @@
package common

type Directive struct {
Name Ident
Args ArgumentList
}
import "github.com/graph-gophers/graphql-go/types"

func ParseDirectives(l *Lexer) DirectiveList {
var directives DirectiveList
func ParseDirectives(l *Lexer) types.DirectiveList {
var directives types.DirectiveList
for l.Peek() == '@' {
l.ConsumeToken('@')
d := &Directive{}
d := &types.Directive{}
d.Name = l.ConsumeIdentWithLoc()
d.Name.Loc.Column--
if l.Peek() == '(' {
d.Args = ParseArguments(l)
d.Arguments = ParseArgumentList(l)
}
directives = append(directives, d)
}
return directives
}

type DirectiveList []*Directive

func (l DirectiveList) Get(name string) *Directive {
for _, d := range l {
if d.Name.Name == name {
return d
}
}
return nil
}
10 changes: 5 additions & 5 deletions internal/common/lexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"text/scanner"

"github.com/graph-gophers/graphql-go/errors"
"github.com/graph-gophers/graphql-go/types"
)

type syntaxError string
Expand All @@ -30,7 +31,6 @@ func NewLexer(s string, useStringDescriptions bool) *Lexer {
}
sc.Init(strings.NewReader(s))


l := Lexer{sc: sc, useStringDescriptions: useStringDescriptions}
l.sc.Error = l.CatchScannerError

Expand Down Expand Up @@ -119,11 +119,11 @@ func (l *Lexer) ConsumeIdent() string {
return name
}

func (l *Lexer) ConsumeIdentWithLoc() Ident {
func (l *Lexer) ConsumeIdentWithLoc() types.Ident {
loc := l.Location()
name := l.sc.TokenText()
l.ConsumeToken(scanner.Ident)
return Ident{name, loc}
return types.Ident{name, loc}
}

func (l *Lexer) ConsumeKeyword(keyword string) {
Expand All @@ -133,8 +133,8 @@ func (l *Lexer) ConsumeKeyword(keyword string) {
l.ConsumeWhitespace()
}

func (l *Lexer) ConsumeLiteral() *BasicLit {
lit := &BasicLit{Type: l.next, Text: l.sc.TokenText()}
func (l *Lexer) ConsumeLiteral() *types.PrimitiveValue {
lit := &types.PrimitiveValue{Type: l.next, Text: l.sc.TokenText()}
l.ConsumeWhitespace()
return lit
}
Expand Down
166 changes: 9 additions & 157 deletions internal/common/literals.go
Original file line number Diff line number Diff line change
@@ -1,160 +1,12 @@
package common

import (
"strconv"
"strings"
"text/scanner"

"github.com/graph-gophers/graphql-go/errors"
"github.com/graph-gophers/graphql-go/types"
)

type Literal interface {
Value(vars map[string]interface{}) interface{}
String() string
Location() errors.Location
}

type BasicLit struct {
Type rune
Text string
Loc errors.Location
}

func (lit *BasicLit) Value(vars map[string]interface{}) interface{} {
switch lit.Type {
case scanner.Int:
value, err := strconv.ParseInt(lit.Text, 10, 32)
if err != nil {
panic(err)
}
return int32(value)

case scanner.Float:
value, err := strconv.ParseFloat(lit.Text, 64)
if err != nil {
panic(err)
}
return value

case scanner.String:
value, err := strconv.Unquote(lit.Text)
if err != nil {
panic(err)
}
return value

case scanner.Ident:
switch lit.Text {
case "true":
return true
case "false":
return false
default:
return lit.Text
}

default:
panic("invalid literal")
}
}

func (lit *BasicLit) String() string {
return lit.Text
}

func (lit *BasicLit) Location() errors.Location {
return lit.Loc
}

type ListLit struct {
Entries []Literal
Loc errors.Location
}

func (lit *ListLit) Value(vars map[string]interface{}) interface{} {
entries := make([]interface{}, len(lit.Entries))
for i, entry := range lit.Entries {
entries[i] = entry.Value(vars)
}
return entries
}

func (lit *ListLit) String() string {
entries := make([]string, len(lit.Entries))
for i, entry := range lit.Entries {
entries[i] = entry.String()
}
return "[" + strings.Join(entries, ", ") + "]"
}

func (lit *ListLit) Location() errors.Location {
return lit.Loc
}

type ObjectLit struct {
Fields []*ObjectLitField
Loc errors.Location
}

type ObjectLitField struct {
Name Ident
Value Literal
}

func (lit *ObjectLit) Value(vars map[string]interface{}) interface{} {
fields := make(map[string]interface{}, len(lit.Fields))
for _, f := range lit.Fields {
fields[f.Name.Name] = f.Value.Value(vars)
}
return fields
}

func (lit *ObjectLit) String() string {
entries := make([]string, 0, len(lit.Fields))
for _, f := range lit.Fields {
entries = append(entries, f.Name.Name+": "+f.Value.String())
}
return "{" + strings.Join(entries, ", ") + "}"
}

func (lit *ObjectLit) Location() errors.Location {
return lit.Loc
}

type NullLit struct {
Loc errors.Location
}

func (lit *NullLit) Value(vars map[string]interface{}) interface{} {
return nil
}

func (lit *NullLit) String() string {
return "null"
}

func (lit *NullLit) Location() errors.Location {
return lit.Loc
}

type Variable struct {
Name string
Loc errors.Location
}

func (v Variable) Value(vars map[string]interface{}) interface{} {
return vars[v.Name]
}

func (v Variable) String() string {
return "$" + v.Name
}

func (v *Variable) Location() errors.Location {
return v.Loc
}

func ParseLiteral(l *Lexer, constOnly bool) Literal {
func ParseLiteral(l *Lexer, constOnly bool) types.Value {
loc := l.Location()
switch l.Peek() {
case '$':
Expand All @@ -163,12 +15,12 @@ func ParseLiteral(l *Lexer, constOnly bool) Literal {
panic("unreachable")
}
l.ConsumeToken('$')
return &Variable{l.ConsumeIdent(), loc}
return &types.Variable{l.ConsumeIdent(), loc}

case scanner.Int, scanner.Float, scanner.String, scanner.Ident:
lit := l.ConsumeLiteral()
if lit.Type == scanner.Ident && lit.Text == "null" {
return &NullLit{loc}
return &types.NullValue{loc}
}
lit.Loc = loc
return lit
Expand All @@ -180,24 +32,24 @@ func ParseLiteral(l *Lexer, constOnly bool) Literal {
return lit
case '[':
l.ConsumeToken('[')
var list []Literal
var list []types.Value
for l.Peek() != ']' {
list = append(list, ParseLiteral(l, constOnly))
}
l.ConsumeToken(']')
return &ListLit{list, loc}
return &types.ListValue{list, loc}

case '{':
l.ConsumeToken('{')
var fields []*ObjectLitField
var fields []*types.ObjectField
for l.Peek() != '}' {
name := l.ConsumeIdentWithLoc()
l.ConsumeToken(':')
value := ParseLiteral(l, constOnly)
fields = append(fields, &ObjectLitField{name, value})
fields = append(fields, &types.ObjectField{name, value})
}
l.ConsumeToken('}')
return &ObjectLit{fields, loc}
return &types.ObjectValue{fields, loc}

default:
l.SyntaxError("invalid value")
Expand Down
Loading

0 comments on commit bf0a0cc

Please sign in to comment.