Initial QSfera import

This commit is contained in:
Курнат Андрей
2026-06-07 10:20:04 +03:00
commit 2315f25754
16485 changed files with 4826827 additions and 0 deletions
+200
View File
@@ -0,0 +1,200 @@
package parser
import (
"strconv"
"github.com/vektah/gqlparser/v2/ast"
"github.com/vektah/gqlparser/v2/gqlerror"
"github.com/vektah/gqlparser/v2/lexer"
)
type parser struct {
lexer lexer.Lexer
err error
peeked bool
peekToken lexer.Token
peekError error
prev lexer.Token
comment *ast.CommentGroup
commentConsuming bool
tokenCount int
maxTokenLimit int
}
func (p *parser) SetMaxTokenLimit(maxToken int) {
p.maxTokenLimit = maxToken
}
func (p *parser) consumeComment() (*ast.Comment, bool) {
if p.err != nil {
return nil, false
}
tok := p.peek()
if tok.Kind != lexer.Comment {
return nil, false
}
p.next()
return &ast.Comment{
Value: tok.Value,
Position: &tok.Pos,
}, true
}
func (p *parser) consumeCommentGroup() {
if p.err != nil {
return
}
if p.commentConsuming {
return
}
p.commentConsuming = true
var comments []*ast.Comment
for {
comment, ok := p.consumeComment()
if !ok {
break
}
comments = append(comments, comment)
}
p.comment = &ast.CommentGroup{List: comments}
p.commentConsuming = false
}
func (p *parser) peekPos() *ast.Position {
if p.err != nil {
return nil
}
peek := p.peek()
return &peek.Pos
}
func (p *parser) peek() lexer.Token {
if p.err != nil {
return p.prev
}
if !p.peeked {
p.peekToken, p.peekError = p.lexer.ReadToken()
p.peeked = true
if p.peekToken.Kind == lexer.Comment {
p.consumeCommentGroup()
}
}
return p.peekToken
}
func (p *parser) error(tok lexer.Token, format string, args ...any) {
if p.err != nil {
return
}
p.err = gqlerror.ErrorLocf(tok.Pos.Src.Name, tok.Pos.Line, tok.Pos.Column, format, args...)
}
func (p *parser) next() lexer.Token {
if p.err != nil {
return p.prev
}
// Increment the token count before reading the next token
p.tokenCount++
if p.maxTokenLimit != 0 && p.tokenCount > p.maxTokenLimit {
p.err = gqlerror.Errorf("exceeded token limit of %d", p.maxTokenLimit)
return p.prev
}
if p.peeked {
p.peeked = false
p.comment = nil
p.prev, p.err = p.peekToken, p.peekError
} else {
p.prev, p.err = p.lexer.ReadToken()
if p.prev.Kind == lexer.Comment {
p.consumeCommentGroup()
}
}
return p.prev
}
func (p *parser) expectKeyword(value string) (lexer.Token, *ast.CommentGroup) {
tok := p.peek()
comment := p.comment
if tok.Kind == lexer.Name && tok.Value == value {
return p.next(), comment
}
p.error(tok, "Expected %s, found %s", strconv.Quote(value), tok.String())
return tok, comment
}
func (p *parser) expect(kind lexer.Type) (lexer.Token, *ast.CommentGroup) {
tok := p.peek()
comment := p.comment
if tok.Kind == kind {
return p.next(), comment
}
p.error(tok, "Expected %s, found %s", kind, tok.Kind.String())
return tok, comment
}
func (p *parser) skip(kind lexer.Type) bool {
if p.err != nil {
return false
}
tok := p.peek()
if tok.Kind != kind {
return false
}
p.next()
return true
}
func (p *parser) unexpectedError() {
p.unexpectedToken(p.peek())
}
func (p *parser) unexpectedToken(tok lexer.Token) {
p.error(tok, "Unexpected %s", tok.String())
}
func (p *parser) many(start, end lexer.Type, cb func()) {
hasDef := p.skip(start)
if !hasDef {
return
}
for p.peek().Kind != end && p.err == nil {
cb()
}
p.next()
}
func (p *parser) some(start, end lexer.Type, cb func()) *ast.CommentGroup {
hasDef := p.skip(start)
if !hasDef {
return nil
}
called := false
for p.peek().Kind != end && p.err == nil {
called = true
cb()
}
if !called {
p.error(p.peek(), "expected at least one definition, found %s", p.peek().Kind.String())
return nil
}
comment := p.comment
p.next()
return comment
}
+372
View File
@@ -0,0 +1,372 @@
package parser
import (
. "github.com/vektah/gqlparser/v2/ast" //nolint:staticcheck // bad, yeah
"github.com/vektah/gqlparser/v2/lexer"
)
func ParseQuery(source *Source) (*QueryDocument, error) {
p := parser{
lexer: lexer.New(source),
maxTokenLimit: 0, // 0 means unlimited
}
return p.parseQueryDocument(), p.err
}
func ParseQueryWithTokenLimit(source *Source, maxTokenLimit int) (*QueryDocument, error) {
p := parser{
lexer: lexer.New(source),
maxTokenLimit: maxTokenLimit,
}
return p.parseQueryDocument(), p.err
}
func (p *parser) parseQueryDocument() *QueryDocument {
var doc QueryDocument
for p.peek().Kind != lexer.EOF {
if p.err != nil {
return &doc
}
doc.Position = p.peekPos()
switch p.peek().Kind {
case lexer.Name:
switch p.peek().Value {
case "query", "mutation", "subscription":
doc.Operations = append(doc.Operations, p.parseOperationDefinition())
case "fragment":
doc.Fragments = append(doc.Fragments, p.parseFragmentDefinition())
default:
p.unexpectedError()
}
case lexer.BraceL:
doc.Operations = append(doc.Operations, p.parseOperationDefinition())
default:
p.unexpectedError()
}
}
return &doc
}
func (p *parser) parseOperationDefinition() *OperationDefinition {
if p.peek().Kind == lexer.BraceL {
return &OperationDefinition{
Position: p.peekPos(),
Comment: p.comment,
Operation: Query,
SelectionSet: p.parseRequiredSelectionSet(),
}
}
var od OperationDefinition
od.Position = p.peekPos()
od.Comment = p.comment
od.Operation = p.parseOperationType()
if p.peek().Kind == lexer.Name {
od.Name = p.next().Value
}
od.VariableDefinitions = p.parseVariableDefinitions()
od.Directives = p.parseDirectives(false)
od.SelectionSet = p.parseRequiredSelectionSet()
return &od
}
func (p *parser) parseOperationType() Operation {
tok := p.next()
switch tok.Value {
case "query":
return Query
case "mutation":
return Mutation
case "subscription":
return Subscription
}
p.unexpectedToken(tok)
return ""
}
func (p *parser) parseVariableDefinitions() VariableDefinitionList {
var defs []*VariableDefinition
p.some(lexer.ParenL, lexer.ParenR, func() {
defs = append(defs, p.parseVariableDefinition())
})
return defs
}
func (p *parser) parseVariableDefinition() *VariableDefinition {
var def VariableDefinition
def.Position = p.peekPos()
def.Comment = p.comment
def.Variable = p.parseVariable()
p.expect(lexer.Colon)
def.Type = p.parseTypeReference()
if p.skip(lexer.Equals) {
def.DefaultValue = p.parseValueLiteral(true)
}
def.Directives = p.parseDirectives(false)
return &def
}
func (p *parser) parseVariable() string {
p.expect(lexer.Dollar)
return p.parseName()
}
func (p *parser) parseOptionalSelectionSet() SelectionSet {
var selections []Selection
p.some(lexer.BraceL, lexer.BraceR, func() {
selections = append(selections, p.parseSelection())
})
return selections
}
func (p *parser) parseRequiredSelectionSet() SelectionSet {
if p.peek().Kind != lexer.BraceL {
p.error(p.peek(), "Expected %s, found %s", lexer.BraceL, p.peek().Kind.String())
return nil
}
var selections []Selection
p.some(lexer.BraceL, lexer.BraceR, func() {
selections = append(selections, p.parseSelection())
})
return selections
}
func (p *parser) parseSelection() Selection {
if p.peek().Kind == lexer.Spread {
return p.parseFragment()
}
return p.parseField()
}
func (p *parser) parseField() *Field {
var field Field
field.Position = p.peekPos()
field.Comment = p.comment
field.Alias = p.parseName()
if p.skip(lexer.Colon) {
field.Name = p.parseName()
} else {
field.Name = field.Alias
}
field.Arguments = p.parseArguments(false)
field.Directives = p.parseDirectives(false)
if p.peek().Kind == lexer.BraceL {
field.SelectionSet = p.parseOptionalSelectionSet()
}
return &field
}
func (p *parser) parseArguments(isConst bool) ArgumentList {
var arguments ArgumentList
p.some(lexer.ParenL, lexer.ParenR, func() {
arguments = append(arguments, p.parseArgument(isConst))
})
return arguments
}
func (p *parser) parseArgument(isConst bool) *Argument {
arg := Argument{}
arg.Position = p.peekPos()
arg.Comment = p.comment
arg.Name = p.parseName()
p.expect(lexer.Colon)
arg.Value = p.parseValueLiteral(isConst)
return &arg
}
func (p *parser) parseFragment() Selection {
_, comment := p.expect(lexer.Spread)
if peek := p.peek(); peek.Kind == lexer.Name && peek.Value != "on" {
return &FragmentSpread{
Position: p.peekPos(),
Comment: comment,
Name: p.parseFragmentName(),
Directives: p.parseDirectives(false),
}
}
var def InlineFragment
def.Position = p.peekPos()
def.Comment = comment
if p.peek().Value == "on" {
p.next() // "on"
def.TypeCondition = p.parseName()
}
def.Directives = p.parseDirectives(false)
def.SelectionSet = p.parseRequiredSelectionSet()
return &def
}
func (p *parser) parseFragmentDefinition() *FragmentDefinition {
var def FragmentDefinition
def.Position = p.peekPos()
def.Comment = p.comment
p.expectKeyword("fragment")
def.Name = p.parseFragmentName()
def.VariableDefinition = p.parseVariableDefinitions()
p.expectKeyword("on")
def.TypeCondition = p.parseName()
def.Directives = p.parseDirectives(false)
def.SelectionSet = p.parseRequiredSelectionSet()
return &def
}
func (p *parser) parseFragmentName() string {
if p.peek().Value == "on" {
p.unexpectedError()
return ""
}
return p.parseName()
}
func (p *parser) parseValueLiteral(isConst bool) *Value {
token := p.peek()
var kind ValueKind
switch token.Kind {
case lexer.BracketL:
return p.parseList(isConst)
case lexer.BraceL:
return p.parseObject(isConst)
case lexer.Dollar:
if isConst {
p.unexpectedError()
return nil
}
return &Value{
Position: &token.Pos,
Comment: p.comment,
Raw: p.parseVariable(),
Kind: Variable,
}
case lexer.Int:
kind = IntValue
case lexer.Float:
kind = FloatValue
case lexer.String:
kind = StringValue
case lexer.BlockString:
kind = BlockValue
case lexer.Name:
switch token.Value {
case "true", "false":
kind = BooleanValue
case "null":
kind = NullValue
default:
kind = EnumValue
}
default:
p.unexpectedError()
return nil
}
p.next()
return &Value{Position: &token.Pos, Comment: p.comment, Raw: token.Value, Kind: kind}
}
func (p *parser) parseList(isConst bool) *Value {
var values ChildValueList
pos := p.peekPos()
comment := p.comment
p.many(lexer.BracketL, lexer.BracketR, func() {
values = append(values, &ChildValue{Value: p.parseValueLiteral(isConst)})
})
return &Value{Children: values, Kind: ListValue, Position: pos, Comment: comment}
}
func (p *parser) parseObject(isConst bool) *Value {
var fields ChildValueList
pos := p.peekPos()
comment := p.comment
p.many(lexer.BraceL, lexer.BraceR, func() {
fields = append(fields, p.parseObjectField(isConst))
})
return &Value{Children: fields, Kind: ObjectValue, Position: pos, Comment: comment}
}
func (p *parser) parseObjectField(isConst bool) *ChildValue {
field := ChildValue{}
field.Position = p.peekPos()
field.Comment = p.comment
field.Name = p.parseName()
p.expect(lexer.Colon)
field.Value = p.parseValueLiteral(isConst)
return &field
}
func (p *parser) parseDirectives(isConst bool) []*Directive {
var directives []*Directive
for p.peek().Kind == lexer.At {
if p.err != nil {
break
}
directives = append(directives, p.parseDirective(isConst))
}
return directives
}
func (p *parser) parseDirective(isConst bool) *Directive {
p.expect(lexer.At)
return &Directive{
Position: p.peekPos(),
Name: p.parseName(),
Arguments: p.parseArguments(isConst),
}
}
func (p *parser) parseTypeReference() *Type {
var typ Type
if p.skip(lexer.BracketL) {
typ.Position = p.peekPos()
typ.Elem = p.parseTypeReference()
p.expect(lexer.BracketR)
} else {
typ.Position = p.peekPos()
typ.NamedType = p.parseName()
}
if p.skip(lexer.Bang) {
typ.NonNull = true
}
return &typ
}
func (p *parser) parseName() string {
token, _ := p.expect(lexer.Name)
return token.Value
}
+545
View File
@@ -0,0 +1,545 @@
parser provides useful errors:
- name: unclosed paren
input: '{'
error:
message: "Expected Name, found <EOF>"
locations: [{line: 1, column: 2}]
- name: missing on in fragment
input: |
{ ...MissingOn }
fragment MissingOn Type
error:
message: 'Expected "on", found Name "Type"'
locations: [{ line: 2, column: 20 }]
- name: missing name after alias
input: '{ field: {} }'
error:
message: "Expected Name, found {"
locations: [{ line: 1, column: 10 }]
- name: not an operation
input: 'notanoperation Foo { field }'
error:
message: 'Unexpected Name "notanoperation"'
locations: [{ line: 1, column: 1 }]
- name: a wild splat appears
input: '...'
error:
message: 'Unexpected ...'
locations: [{ line: 1, column: 1}]
variables:
- name: are allowed in args
input: '{ field(complex: { a: { b: [ $var ] } }) }'
- name: are not allowed in default args
input: 'query Foo($x: Complex = { a: { b: [ $var ] } }) { field }'
error:
message: 'Unexpected $'
locations: [{ line: 1, column: 37 }]
- name: can have directives
input: 'query ($withDirective: String @first @second, $withoutDirective: String) { f }'
ast: |
<QueryDocument>
Operations: [OperationDefinition]
- <OperationDefinition>
Operation: Operation("query")
VariableDefinitions: [VariableDefinition]
- <VariableDefinition>
Variable: "withDirective"
Type: String
Directives: [Directive]
- <Directive>
Name: "first"
- <Directive>
Name: "second"
- <VariableDefinition>
Variable: "withoutDirective"
Type: String
SelectionSet: [Selection]
- <Field>
Alias: "f"
Name: "f"
fragments:
- name: can not be named 'on'
input: 'fragment on on on { on }'
error:
message: 'Unexpected Name "on"'
locations: [{ line: 1, column: 10 }]
- name: can not spread fragments called 'on'
input: '{ ...on }'
error:
message: 'Expected Name, found }'
locations: [{ line: 1, column: 9 }]
encoding:
- name: multibyte characters are supported
input: |
# This comment has a ਊ multi-byte character.
{ field(arg: "Has a ਊ multi-byte character.") }
ast: |
<QueryDocument>
Operations: [OperationDefinition]
- <OperationDefinition>
Operation: Operation("query")
SelectionSet: [Selection]
- <Field>
Alias: "field"
Name: "field"
Arguments: [Argument]
- <Argument>
Name: "arg"
Value: "Has a ਊ multi-byte character."
keywords are allowed anywhere a name is:
- name: on
input: |
query on {
... a
... on on { field }
}
fragment a on Type {
on(on: $on)
@on(on: on)
}
- name: subscription
input: |
query subscription {
... subscription
... on subscription { field }
}
fragment subscription on Type {
subscription(subscription: $subscription)
@subscription(subscription: subscription)
}
- name: true
input: |
query true {
... true
... on true { field }
}
fragment true on Type {
true(true: $true)
@true(true: true)
}
operations:
- name: anonymous mutation
input: 'mutation { mutationField }'
- name: named mutation
input: 'mutation Foo { mutationField }'
- name: anonymous subscription
input: 'subscription { subscriptionField }'
- name: named subscription
input: 'subscription Foo { subscriptionField }'
ast:
- name: simple query
input: |
{
node(id: 4) {
id,
name
}
}
ast: |
<QueryDocument>
Operations: [OperationDefinition]
- <OperationDefinition>
Operation: Operation("query")
SelectionSet: [Selection]
- <Field>
Alias: "node"
Name: "node"
Arguments: [Argument]
- <Argument>
Name: "id"
Value: 4
SelectionSet: [Selection]
- <Field>
Alias: "id"
Name: "id"
- <Field>
Alias: "name"
Name: "name"
- name: nameless query with no variables
input: |
query {
node {
id
}
}
ast: |
<QueryDocument>
Operations: [OperationDefinition]
- <OperationDefinition>
Operation: Operation("query")
SelectionSet: [Selection]
- <Field>
Alias: "node"
Name: "node"
SelectionSet: [Selection]
- <Field>
Alias: "id"
Name: "id"
- name: fragment defined variables
input: 'fragment a($v: Boolean = false) on t { f(v: $v) }'
ast: |
<QueryDocument>
Fragments: [FragmentDefinition]
- <FragmentDefinition>
Name: "a"
VariableDefinition: [VariableDefinition]
- <VariableDefinition>
Variable: "v"
Type: Boolean
DefaultValue: false
TypeCondition: "t"
SelectionSet: [Selection]
- <Field>
Alias: "f"
Name: "f"
Arguments: [Argument]
- <Argument>
Name: "v"
Value: $v
values:
- name: null
input: '{ f(id: null) }'
ast: |
<QueryDocument>
Operations: [OperationDefinition]
- <OperationDefinition>
Operation: Operation("query")
SelectionSet: [Selection]
- <Field>
Alias: "f"
Name: "f"
Arguments: [Argument]
- <Argument>
Name: "id"
Value: null
- name: strings
input: '{ f(long: """long""", short: "short") } '
ast: |
<QueryDocument>
Operations: [OperationDefinition]
- <OperationDefinition>
Operation: Operation("query")
SelectionSet: [Selection]
- <Field>
Alias: "f"
Name: "f"
Arguments: [Argument]
- <Argument>
Name: "long"
Value: "long"
- <Argument>
Name: "short"
Value: "short"
- name: list
input: '{ f(id: [1,2]) }'
ast: |
<QueryDocument>
Operations: [OperationDefinition]
- <OperationDefinition>
Operation: Operation("query")
SelectionSet: [Selection]
- <Field>
Alias: "f"
Name: "f"
Arguments: [Argument]
- <Argument>
Name: "id"
Value: [1,2]
types:
- name: common types
input: 'query ($string: String, $int: Int, $arr: [Arr], $notnull: [Arr!]!) { f }'
ast: |
<QueryDocument>
Operations: [OperationDefinition]
- <OperationDefinition>
Operation: Operation("query")
VariableDefinitions: [VariableDefinition]
- <VariableDefinition>
Variable: "string"
Type: String
- <VariableDefinition>
Variable: "int"
Type: Int
- <VariableDefinition>
Variable: "arr"
Type: [Arr]
- <VariableDefinition>
Variable: "notnull"
Type: [Arr!]!
SelectionSet: [Selection]
- <Field>
Alias: "f"
Name: "f"
large queries:
- name: kitchen sink
input: |
# Copyright (c) 2015-present, Facebook, Inc.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
query queryName($foo: ComplexType, $site: Site = MOBILE) {
whoever123is: node(id: [123, 456]) {
id ,
... on User @defer {
field2 {
id ,
alias: field1(first:10, after:$foo,) @include(if: $foo) {
id,
...frag
}
}
}
... @skip(unless: $foo) {
id
}
... {
id
}
}
}
mutation likeStory {
like(story: 123) @defer {
story {
id
}
}
}
subscription StoryLikeSubscription($input: StoryLikeSubscribeInput) {
storyLikeSubscribe(input: $input) {
story {
likers {
count
}
likeSentence {
text
}
}
}
}
fragment frag on Friend {
foo(size: $size, bar: $b, obj: {key: "value", block: """
block string uses \"""
"""})
}
{
unnamed(truthy: true, falsey: false, nullish: null),
query
}
ast: |
<QueryDocument>
Operations: [OperationDefinition]
- <OperationDefinition>
Operation: Operation("query")
Name: "queryName"
VariableDefinitions: [VariableDefinition]
- <VariableDefinition>
Variable: "foo"
Type: ComplexType
- <VariableDefinition>
Variable: "site"
Type: Site
DefaultValue: MOBILE
SelectionSet: [Selection]
- <Field>
Alias: "whoever123is"
Name: "node"
Arguments: [Argument]
- <Argument>
Name: "id"
Value: [123,456]
SelectionSet: [Selection]
- <Field>
Alias: "id"
Name: "id"
- <InlineFragment>
TypeCondition: "User"
Directives: [Directive]
- <Directive>
Name: "defer"
SelectionSet: [Selection]
- <Field>
Alias: "field2"
Name: "field2"
SelectionSet: [Selection]
- <Field>
Alias: "id"
Name: "id"
- <Field>
Alias: "alias"
Name: "field1"
Arguments: [Argument]
- <Argument>
Name: "first"
Value: 10
- <Argument>
Name: "after"
Value: $foo
Directives: [Directive]
- <Directive>
Name: "include"
Arguments: [Argument]
- <Argument>
Name: "if"
Value: $foo
SelectionSet: [Selection]
- <Field>
Alias: "id"
Name: "id"
- <FragmentSpread>
Name: "frag"
- <InlineFragment>
Directives: [Directive]
- <Directive>
Name: "skip"
Arguments: [Argument]
- <Argument>
Name: "unless"
Value: $foo
SelectionSet: [Selection]
- <Field>
Alias: "id"
Name: "id"
- <InlineFragment>
SelectionSet: [Selection]
- <Field>
Alias: "id"
Name: "id"
Comment: "# Copyright (c) 2015-present, Facebook, Inc.\n#\n# This source code is licensed under the MIT license found in the\n# LICENSE file in the root directory of this source tree.\n"
- <OperationDefinition>
Operation: Operation("mutation")
Name: "likeStory"
SelectionSet: [Selection]
- <Field>
Alias: "like"
Name: "like"
Arguments: [Argument]
- <Argument>
Name: "story"
Value: 123
Directives: [Directive]
- <Directive>
Name: "defer"
SelectionSet: [Selection]
- <Field>
Alias: "story"
Name: "story"
SelectionSet: [Selection]
- <Field>
Alias: "id"
Name: "id"
- <OperationDefinition>
Operation: Operation("subscription")
Name: "StoryLikeSubscription"
VariableDefinitions: [VariableDefinition]
- <VariableDefinition>
Variable: "input"
Type: StoryLikeSubscribeInput
SelectionSet: [Selection]
- <Field>
Alias: "storyLikeSubscribe"
Name: "storyLikeSubscribe"
Arguments: [Argument]
- <Argument>
Name: "input"
Value: $input
SelectionSet: [Selection]
- <Field>
Alias: "story"
Name: "story"
SelectionSet: [Selection]
- <Field>
Alias: "likers"
Name: "likers"
SelectionSet: [Selection]
- <Field>
Alias: "count"
Name: "count"
- <Field>
Alias: "likeSentence"
Name: "likeSentence"
SelectionSet: [Selection]
- <Field>
Alias: "text"
Name: "text"
- <OperationDefinition>
Operation: Operation("query")
SelectionSet: [Selection]
- <Field>
Alias: "unnamed"
Name: "unnamed"
Arguments: [Argument]
- <Argument>
Name: "truthy"
Value: true
- <Argument>
Name: "falsey"
Value: false
- <Argument>
Name: "nullish"
Value: null
- <Field>
Alias: "query"
Name: "query"
Fragments: [FragmentDefinition]
- <FragmentDefinition>
Name: "frag"
TypeCondition: "Friend"
SelectionSet: [Selection]
- <Field>
Alias: "foo"
Name: "foo"
Arguments: [Argument]
- <Argument>
Name: "size"
Value: $size
- <Argument>
Name: "bar"
Value: $b
- <Argument>
Name: "obj"
Value: {key:"value",block:"block string uses \"\"\""}
fuzzer:
- name: 01
input: '{__typename{...}}'
error:
message: 'Expected {, found }'
locations: [{ line: 1, column: 16 }]
- name: 02
input: '{...{__typename{...{}}}}'
error:
message: 'expected at least one definition, found }'
locations: [{ line: 1, column: 21 }]
+635
View File
@@ -0,0 +1,635 @@
package parser
import (
. "github.com/vektah/gqlparser/v2/ast" //nolint:staticcheck // bad, yeah
"github.com/vektah/gqlparser/v2/lexer"
)
func ParseSchemas(inputs ...*Source) (*SchemaDocument, error) {
sd := &SchemaDocument{}
for _, input := range inputs {
inputAst, err := ParseSchema(input)
if err != nil {
return nil, err
}
sd.Merge(inputAst)
}
return sd, nil
}
func ParseSchema(source *Source) (*SchemaDocument, error) {
p := parser{
lexer: lexer.New(source),
maxTokenLimit: 0, // default value is unlimited
}
sd, err := p.parseSchemaDocument(), p.err
if err != nil {
return nil, err
}
for _, def := range sd.Definitions {
def.BuiltIn = source.BuiltIn
}
for _, def := range sd.Extensions {
def.BuiltIn = source.BuiltIn
}
return sd, nil
}
func ParseSchemasWithLimit(maxTokenLimit int, inputs ...*Source) (*SchemaDocument, error) {
sd := &SchemaDocument{}
for _, input := range inputs {
inputAst, err := ParseSchemaWithLimit(input, maxTokenLimit)
if err != nil {
return nil, err
}
sd.Merge(inputAst)
}
return sd, nil
}
func ParseSchemaWithLimit(source *Source, maxTokenLimit int) (*SchemaDocument, error) {
p := parser{
lexer: lexer.New(source),
maxTokenLimit: maxTokenLimit, // 0 is unlimited
}
sd, err := p.parseSchemaDocument(), p.err
if err != nil {
return nil, err
}
for _, def := range sd.Definitions {
def.BuiltIn = source.BuiltIn
}
for _, def := range sd.Extensions {
def.BuiltIn = source.BuiltIn
}
return sd, nil
}
func (p *parser) parseSchemaDocument() *SchemaDocument {
var doc SchemaDocument
doc.Position = p.peekPos()
for p.peek().Kind != lexer.EOF {
if p.err != nil {
return nil
}
var description descriptionWithComment
if p.peek().Kind == lexer.BlockString || p.peek().Kind == lexer.String {
description = p.parseDescription()
}
if p.peek().Kind != lexer.Name {
p.unexpectedError()
break
}
switch p.peek().Value {
case "scalar", "type", "interface", "union", "enum", "input":
doc.Definitions = append(doc.Definitions, p.parseTypeSystemDefinition(description))
case "schema":
doc.Schema = append(doc.Schema, p.parseSchemaDefinition(description))
case "directive":
doc.Directives = append(doc.Directives, p.parseDirectiveDefinition(description))
case "extend":
if description.text != "" {
p.unexpectedToken(p.prev)
}
p.parseTypeSystemExtension(&doc)
default:
p.unexpectedError()
return nil
}
}
// treat end of file comments
doc.Comment = p.comment
return &doc
}
func (p *parser) parseDescription() descriptionWithComment {
token := p.peek()
var desc descriptionWithComment
if token.Kind != lexer.BlockString && token.Kind != lexer.String {
return desc
}
desc.comment = p.comment
desc.text = p.next().Value
return desc
}
func (p *parser) parseTypeSystemDefinition(description descriptionWithComment) *Definition {
tok := p.peek()
if tok.Kind != lexer.Name {
p.unexpectedError()
return nil
}
switch tok.Value {
case "scalar":
return p.parseScalarTypeDefinition(description)
case "type":
return p.parseObjectTypeDefinition(description)
case "interface":
return p.parseInterfaceTypeDefinition(description)
case "union":
return p.parseUnionTypeDefinition(description)
case "enum":
return p.parseEnumTypeDefinition(description)
case "input":
return p.parseInputObjectTypeDefinition(description)
default:
p.unexpectedError()
return nil
}
}
func (p *parser) parseSchemaDefinition(description descriptionWithComment) *SchemaDefinition {
_, comment := p.expectKeyword("schema")
def := SchemaDefinition{}
def.Position = p.peekPos()
def.BeforeDescriptionComment = description.comment
def.Description = description.text
def.AfterDescriptionComment = comment
def.Directives = p.parseDirectives(true)
def.EndOfDefinitionComment = p.some(lexer.BraceL, lexer.BraceR, func() {
def.OperationTypes = append(def.OperationTypes, p.parseOperationTypeDefinition())
})
return &def
}
func (p *parser) parseOperationTypeDefinition() *OperationTypeDefinition {
var op OperationTypeDefinition
op.Position = p.peekPos()
op.Comment = p.comment
op.Operation = p.parseOperationType()
p.expect(lexer.Colon)
op.Type = p.parseName()
return &op
}
func (p *parser) parseScalarTypeDefinition(description descriptionWithComment) *Definition {
_, comment := p.expectKeyword("scalar")
var def Definition
def.Position = p.peekPos()
def.BeforeDescriptionComment = description.comment
def.Description = description.text
def.AfterDescriptionComment = comment
def.Kind = Scalar
def.Name = p.parseName()
def.Directives = p.parseDirectives(true)
return &def
}
func (p *parser) parseObjectTypeDefinition(description descriptionWithComment) *Definition {
_, comment := p.expectKeyword("type")
var def Definition
def.Position = p.peekPos()
def.Kind = Object
def.BeforeDescriptionComment = description.comment
def.Description = description.text
def.AfterDescriptionComment = comment
def.Name = p.parseName()
def.Interfaces = p.parseImplementsInterfaces()
def.Directives = p.parseDirectives(true)
def.Fields, def.EndOfDefinitionComment = p.parseFieldsDefinition()
return &def
}
func (p *parser) parseImplementsInterfaces() []string {
var types []string
if p.peek().Value == "implements" {
p.next()
// optional leading ampersand
p.skip(lexer.Amp)
types = append(types, p.parseName())
for p.skip(lexer.Amp) && p.err == nil {
types = append(types, p.parseName())
}
}
return types
}
func (p *parser) parseFieldsDefinition() (FieldList, *CommentGroup) {
var defs FieldList
comment := p.some(lexer.BraceL, lexer.BraceR, func() {
defs = append(defs, p.parseFieldDefinition())
})
return defs, comment
}
func (p *parser) parseFieldDefinition() *FieldDefinition {
var def FieldDefinition
def.Position = p.peekPos()
desc := p.parseDescription()
if desc.text != "" {
def.BeforeDescriptionComment = desc.comment
def.Description = desc.text
}
p.peek() // peek to set p.comment
def.AfterDescriptionComment = p.comment
def.Name = p.parseName()
def.Arguments = p.parseArgumentDefs()
p.expect(lexer.Colon)
def.Type = p.parseTypeReference()
def.Directives = p.parseDirectives(true)
return &def
}
func (p *parser) parseArgumentDefs() ArgumentDefinitionList {
var args ArgumentDefinitionList
p.some(lexer.ParenL, lexer.ParenR, func() {
args = append(args, p.parseArgumentDef())
})
return args
}
func (p *parser) parseArgumentDef() *ArgumentDefinition {
var def ArgumentDefinition
def.Position = p.peekPos()
desc := p.parseDescription()
if desc.text != "" {
def.BeforeDescriptionComment = desc.comment
def.Description = desc.text
}
p.peek() // peek to set p.comment
def.AfterDescriptionComment = p.comment
def.Name = p.parseName()
p.expect(lexer.Colon)
def.Type = p.parseTypeReference()
if p.skip(lexer.Equals) {
def.DefaultValue = p.parseValueLiteral(true)
}
def.Directives = p.parseDirectives(true)
return &def
}
func (p *parser) parseInputValueDef() *FieldDefinition {
var def FieldDefinition
def.Position = p.peekPos()
desc := p.parseDescription()
if desc.text != "" {
def.BeforeDescriptionComment = desc.comment
def.Description = desc.text
}
p.peek() // peek to set p.comment
def.AfterDescriptionComment = p.comment
def.Name = p.parseName()
p.expect(lexer.Colon)
def.Type = p.parseTypeReference()
if p.skip(lexer.Equals) {
def.DefaultValue = p.parseValueLiteral(true)
}
def.Directives = p.parseDirectives(true)
return &def
}
func (p *parser) parseInterfaceTypeDefinition(description descriptionWithComment) *Definition {
_, comment := p.expectKeyword("interface")
var def Definition
def.Position = p.peekPos()
def.Kind = Interface
def.BeforeDescriptionComment = description.comment
def.Description = description.text
def.AfterDescriptionComment = comment
def.Name = p.parseName()
def.Interfaces = p.parseImplementsInterfaces()
def.Directives = p.parseDirectives(true)
def.Fields, def.EndOfDefinitionComment = p.parseFieldsDefinition()
return &def
}
func (p *parser) parseUnionTypeDefinition(description descriptionWithComment) *Definition {
_, comment := p.expectKeyword("union")
var def Definition
def.Position = p.peekPos()
def.Kind = Union
def.BeforeDescriptionComment = description.comment
def.Description = description.text
def.AfterDescriptionComment = comment
def.Name = p.parseName()
def.Directives = p.parseDirectives(true)
def.Types = p.parseUnionMemberTypes()
return &def
}
func (p *parser) parseUnionMemberTypes() []string {
var types []string
if p.skip(lexer.Equals) {
// optional leading pipe
p.skip(lexer.Pipe)
types = append(types, p.parseName())
for p.skip(lexer.Pipe) && p.err == nil {
types = append(types, p.parseName())
}
}
return types
}
func (p *parser) parseEnumTypeDefinition(description descriptionWithComment) *Definition {
_, comment := p.expectKeyword("enum")
var def Definition
def.Position = p.peekPos()
def.Kind = Enum
def.BeforeDescriptionComment = description.comment
def.Description = description.text
def.AfterDescriptionComment = comment
def.Name = p.parseName()
def.Directives = p.parseDirectives(true)
def.EnumValues, def.EndOfDefinitionComment = p.parseEnumValuesDefinition()
return &def
}
func (p *parser) parseEnumValuesDefinition() (EnumValueList, *CommentGroup) {
var values EnumValueList
comment := p.some(lexer.BraceL, lexer.BraceR, func() {
values = append(values, p.parseEnumValueDefinition())
})
return values, comment
}
func (p *parser) parseEnumValueDefinition() *EnumValueDefinition {
var def EnumValueDefinition
def.Position = p.peekPos()
desc := p.parseDescription()
if desc.text != "" {
def.BeforeDescriptionComment = desc.comment
def.Description = desc.text
}
p.peek() // peek to set p.comment
def.AfterDescriptionComment = p.comment
def.Name = p.parseName()
def.Directives = p.parseDirectives(true)
return &def
}
func (p *parser) parseInputObjectTypeDefinition(description descriptionWithComment) *Definition {
_, comment := p.expectKeyword("input")
var def Definition
def.Position = p.peekPos()
def.Kind = InputObject
def.BeforeDescriptionComment = description.comment
def.Description = description.text
def.AfterDescriptionComment = comment
def.Name = p.parseName()
def.Directives = p.parseDirectives(true)
def.Fields, def.EndOfDefinitionComment = p.parseInputFieldsDefinition()
return &def
}
func (p *parser) parseInputFieldsDefinition() (FieldList, *CommentGroup) {
var values FieldList
comment := p.some(lexer.BraceL, lexer.BraceR, func() {
values = append(values, p.parseInputValueDef())
})
return values, comment
}
func (p *parser) parseTypeSystemExtension(doc *SchemaDocument) {
_, comment := p.expectKeyword("extend")
switch p.peek().Value {
case "schema":
doc.SchemaExtension = append(doc.SchemaExtension, p.parseSchemaExtension(comment))
case "scalar":
doc.Extensions = append(doc.Extensions, p.parseScalarTypeExtension(comment))
case "type":
doc.Extensions = append(doc.Extensions, p.parseObjectTypeExtension(comment))
case "interface":
doc.Extensions = append(doc.Extensions, p.parseInterfaceTypeExtension(comment))
case "union":
doc.Extensions = append(doc.Extensions, p.parseUnionTypeExtension(comment))
case "enum":
doc.Extensions = append(doc.Extensions, p.parseEnumTypeExtension(comment))
case "input":
doc.Extensions = append(doc.Extensions, p.parseInputObjectTypeExtension(comment))
default:
p.unexpectedError()
}
}
func (p *parser) parseSchemaExtension(comment *CommentGroup) *SchemaDefinition {
p.expectKeyword("schema")
var def SchemaDefinition
def.Position = p.peekPos()
def.AfterDescriptionComment = comment
def.Directives = p.parseDirectives(true)
def.EndOfDefinitionComment = p.some(lexer.BraceL, lexer.BraceR, func() {
def.OperationTypes = append(def.OperationTypes, p.parseOperationTypeDefinition())
})
if len(def.Directives) == 0 && len(def.OperationTypes) == 0 {
p.unexpectedError()
}
return &def
}
func (p *parser) parseScalarTypeExtension(comment *CommentGroup) *Definition {
p.expectKeyword("scalar")
var def Definition
def.Position = p.peekPos()
def.AfterDescriptionComment = comment
def.Kind = Scalar
def.Name = p.parseName()
def.Directives = p.parseDirectives(true)
if len(def.Directives) == 0 {
p.unexpectedError()
}
return &def
}
func (p *parser) parseObjectTypeExtension(comment *CommentGroup) *Definition {
p.expectKeyword("type")
var def Definition
def.Position = p.peekPos()
def.AfterDescriptionComment = comment
def.Kind = Object
def.Name = p.parseName()
def.Interfaces = p.parseImplementsInterfaces()
def.Directives = p.parseDirectives(true)
def.Fields, def.EndOfDefinitionComment = p.parseFieldsDefinition()
if len(def.Interfaces) == 0 && len(def.Directives) == 0 && len(def.Fields) == 0 {
p.unexpectedError()
}
return &def
}
func (p *parser) parseInterfaceTypeExtension(comment *CommentGroup) *Definition {
p.expectKeyword("interface")
var def Definition
def.Position = p.peekPos()
def.AfterDescriptionComment = comment
def.Kind = Interface
def.Name = p.parseName()
def.Directives = p.parseDirectives(true)
def.Fields, def.EndOfDefinitionComment = p.parseFieldsDefinition()
if len(def.Directives) == 0 && len(def.Fields) == 0 {
p.unexpectedError()
}
return &def
}
func (p *parser) parseUnionTypeExtension(comment *CommentGroup) *Definition {
p.expectKeyword("union")
var def Definition
def.Position = p.peekPos()
def.AfterDescriptionComment = comment
def.Kind = Union
def.Name = p.parseName()
def.Directives = p.parseDirectives(true)
def.Types = p.parseUnionMemberTypes()
if len(def.Directives) == 0 && len(def.Types) == 0 {
p.unexpectedError()
}
return &def
}
func (p *parser) parseEnumTypeExtension(comment *CommentGroup) *Definition {
p.expectKeyword("enum")
var def Definition
def.Position = p.peekPos()
def.AfterDescriptionComment = comment
def.Kind = Enum
def.Name = p.parseName()
def.Directives = p.parseDirectives(true)
def.EnumValues, def.EndOfDefinitionComment = p.parseEnumValuesDefinition()
if len(def.Directives) == 0 && len(def.EnumValues) == 0 {
p.unexpectedError()
}
return &def
}
func (p *parser) parseInputObjectTypeExtension(comment *CommentGroup) *Definition {
p.expectKeyword("input")
var def Definition
def.Position = p.peekPos()
def.AfterDescriptionComment = comment
def.Kind = InputObject
def.Name = p.parseName()
def.Directives = p.parseDirectives(false)
def.Fields, def.EndOfDefinitionComment = p.parseInputFieldsDefinition()
if len(def.Directives) == 0 && len(def.Fields) == 0 {
p.unexpectedError()
}
return &def
}
func (p *parser) parseDirectiveDefinition(description descriptionWithComment) *DirectiveDefinition {
_, comment := p.expectKeyword("directive")
p.expect(lexer.At)
var def DirectiveDefinition
def.Position = p.peekPos()
def.BeforeDescriptionComment = description.comment
def.Description = description.text
def.AfterDescriptionComment = comment
def.Name = p.parseName()
def.Arguments = p.parseArgumentDefs()
if peek := p.peek(); peek.Kind == lexer.Name && peek.Value == "repeatable" {
def.IsRepeatable = true
p.skip(lexer.Name)
}
p.expectKeyword("on")
def.Locations = p.parseDirectiveLocations()
return &def
}
func (p *parser) parseDirectiveLocations() []DirectiveLocation {
p.skip(lexer.Pipe)
locations := []DirectiveLocation{p.parseDirectiveLocation()}
for p.skip(lexer.Pipe) && p.err == nil {
locations = append(locations, p.parseDirectiveLocation())
}
return locations
}
func (p *parser) parseDirectiveLocation() DirectiveLocation {
name, _ := p.expect(lexer.Name)
switch name.Value {
case `QUERY`:
return LocationQuery
case `MUTATION`:
return LocationMutation
case `SUBSCRIPTION`:
return LocationSubscription
case `FIELD`:
return LocationField
case `FRAGMENT_DEFINITION`:
return LocationFragmentDefinition
case `FRAGMENT_SPREAD`:
return LocationFragmentSpread
case `INLINE_FRAGMENT`:
return LocationInlineFragment
case `VARIABLE_DEFINITION`:
return LocationVariableDefinition
case `SCHEMA`:
return LocationSchema
case `SCALAR`:
return LocationScalar
case `OBJECT`:
return LocationObject
case `FIELD_DEFINITION`:
return LocationFieldDefinition
case `ARGUMENT_DEFINITION`:
return LocationArgumentDefinition
case `INTERFACE`:
return LocationInterface
case `UNION`:
return LocationUnion
case `ENUM`:
return LocationEnum
case `ENUM_VALUE`:
return LocationEnumValue
case `INPUT_OBJECT`:
return LocationInputObject
case `INPUT_FIELD_DEFINITION`:
return LocationInputFieldDefinition
}
p.unexpectedToken(name)
return ""
}
type descriptionWithComment struct {
text string
comment *CommentGroup
}
+760
View File
@@ -0,0 +1,760 @@
object types:
- name: simple
input: |
type Hello {
world: String
}
ast: |
<SchemaDocument>
Definitions: [Definition]
- <Definition>
Kind: DefinitionKind("OBJECT")
Name: "Hello"
Fields: [FieldDefinition]
- <FieldDefinition>
Name: "world"
Type: String
- name: with comments
input: |
# Hello
# Hello another
type Hello {
# World
# World another
world: String
# end of type comments
}
# end of file comments
ast: |
<SchemaDocument>
Definitions: [Definition]
- <Definition>
Kind: DefinitionKind("OBJECT")
Name: "Hello"
Fields: [FieldDefinition]
- <FieldDefinition>
Name: "world"
Type: String
AfterDescriptionComment: "# World\n# World another\n"
AfterDescriptionComment: "# Hello\n# Hello another\n"
EndOfDefinitionComment: "# end of type comments\n"
Comment: "# end of file comments\n"
- name: with comments and description
input: |
# Hello
# Hello another
"type description"
# Hello after description
# Hello after description another
type Hello {
# World
# World another
"field description"
# World after description
# World after description another
world: String
# end of definition coments
# end of definition comments another
}
ast: |
<SchemaDocument>
Definitions: [Definition]
- <Definition>
Kind: DefinitionKind("OBJECT")
Description: "type description"
Name: "Hello"
Fields: [FieldDefinition]
- <FieldDefinition>
Description: "field description"
Name: "world"
Type: String
BeforeDescriptionComment: "# World\n# World another\n"
AfterDescriptionComment: "# World after description\n# World after description another\n"
BeforeDescriptionComment: "# Hello\n# Hello another\n"
AfterDescriptionComment: "# Hello after description\n# Hello after description another\n"
EndOfDefinitionComment: "# end of definition coments\n# end of definition comments another\n"
- name: with description
input: |
"Description"
type Hello {
world: String
}
ast: |
<SchemaDocument>
Definitions: [Definition]
- <Definition>
Kind: DefinitionKind("OBJECT")
Description: "Description"
Name: "Hello"
Fields: [FieldDefinition]
- <FieldDefinition>
Name: "world"
Type: String
- name: with block description
input: |
# Before description comment
"""
Description
"""
# Even with comments between them
type Hello {
world: String
}
ast: |
<SchemaDocument>
Definitions: [Definition]
- <Definition>
Kind: DefinitionKind("OBJECT")
Description: "Description"
Name: "Hello"
Fields: [FieldDefinition]
- <FieldDefinition>
Name: "world"
Type: String
BeforeDescriptionComment: "# Before description comment\n"
AfterDescriptionComment: "# Even with comments between them\n"
- name: with field arg
input: |
type Hello {
world(flag: Boolean): String
}
ast: |
<SchemaDocument>
Definitions: [Definition]
- <Definition>
Kind: DefinitionKind("OBJECT")
Name: "Hello"
Fields: [FieldDefinition]
- <FieldDefinition>
Name: "world"
Arguments: [ArgumentDefinition]
- <ArgumentDefinition>
Name: "flag"
Type: Boolean
Type: String
- name: with field arg and default value
input: |
type Hello {
world(flag: Boolean = true): String
}
ast: |
<SchemaDocument>
Definitions: [Definition]
- <Definition>
Kind: DefinitionKind("OBJECT")
Name: "Hello"
Fields: [FieldDefinition]
- <FieldDefinition>
Name: "world"
Arguments: [ArgumentDefinition]
- <ArgumentDefinition>
Name: "flag"
DefaultValue: true
Type: Boolean
Type: String
- name: with field list arg
input: |
type Hello {
world(things: [String]): String
}
ast: |
<SchemaDocument>
Definitions: [Definition]
- <Definition>
Kind: DefinitionKind("OBJECT")
Name: "Hello"
Fields: [FieldDefinition]
- <FieldDefinition>
Name: "world"
Arguments: [ArgumentDefinition]
- <ArgumentDefinition>
Name: "things"
Type: [String]
Type: String
- name: with two args
input: |
type Hello {
world(argOne: Boolean, argTwo: Int): String
}
ast: |
<SchemaDocument>
Definitions: [Definition]
- <Definition>
Kind: DefinitionKind("OBJECT")
Name: "Hello"
Fields: [FieldDefinition]
- <FieldDefinition>
Name: "world"
Arguments: [ArgumentDefinition]
- <ArgumentDefinition>
Name: "argOne"
Type: Boolean
- <ArgumentDefinition>
Name: "argTwo"
Type: Int
Type: String
- name: must define one or more fields
input: |
type Hello {}
error:
message: "expected at least one definition, found }"
locations: [{ line: 1, column: 13 }]
type extensions:
- name: Object extension
input: |
# comment
extend type Hello {
# comment world
world: String
# end of definition comment
}
ast: |
<SchemaDocument>
Extensions: [Definition]
- <Definition>
Kind: DefinitionKind("OBJECT")
Name: "Hello"
Fields: [FieldDefinition]
- <FieldDefinition>
Name: "world"
Type: String
AfterDescriptionComment: "# comment world\n"
AfterDescriptionComment: "# comment\n"
EndOfDefinitionComment: "# end of definition comment\n"
- name: without any fields
input: "extend type Hello implements Greeting"
ast: |
<SchemaDocument>
Extensions: [Definition]
- <Definition>
Kind: DefinitionKind("OBJECT")
Name: "Hello"
Interfaces: [string]
- "Greeting"
- name: without fields twice
input: |
extend type Hello implements Greeting
extend type Hello implements SecondGreeting
ast: |
<SchemaDocument>
Extensions: [Definition]
- <Definition>
Kind: DefinitionKind("OBJECT")
Name: "Hello"
Interfaces: [string]
- "Greeting"
- <Definition>
Kind: DefinitionKind("OBJECT")
Name: "Hello"
Interfaces: [string]
- "SecondGreeting"
- name: without anything errors
input: "extend type Hello"
error:
message: "Unexpected <EOF>"
locations: [{ line: 1, column: 18 }]
- name: can have descriptions # hmm, this might not be spec compliant...
input: |
"Description"
extend type Hello {
world: String
}
error:
message: 'Unexpected String "Description"'
locations: [{ line: 1, column: 2 }]
- name: can not have descriptions on types
input: |
extend "Description" type Hello {
world: String
}
error:
message: Unexpected String "Description"
locations: [{ line: 1, column: 9 }]
- name: all can have directives
input: |
extend scalar Foo @deprecated
extend type Foo @deprecated
extend interface Foo @deprecated
extend union Foo @deprecated
extend enum Foo @deprecated
extend input Foo @deprecated
ast: |
<SchemaDocument>
Extensions: [Definition]
- <Definition>
Kind: DefinitionKind("SCALAR")
Name: "Foo"
Directives: [Directive]
- <Directive>
Name: "deprecated"
- <Definition>
Kind: DefinitionKind("OBJECT")
Name: "Foo"
Directives: [Directive]
- <Directive>
Name: "deprecated"
- <Definition>
Kind: DefinitionKind("INTERFACE")
Name: "Foo"
Directives: [Directive]
- <Directive>
Name: "deprecated"
- <Definition>
Kind: DefinitionKind("UNION")
Name: "Foo"
Directives: [Directive]
- <Directive>
Name: "deprecated"
- <Definition>
Kind: DefinitionKind("ENUM")
Name: "Foo"
Directives: [Directive]
- <Directive>
Name: "deprecated"
- <Definition>
Kind: DefinitionKind("INPUT_OBJECT")
Name: "Foo"
Directives: [Directive]
- <Directive>
Name: "deprecated"
schema definition:
- name: simple
input: |
schema {
query: Query
}
ast: |
<SchemaDocument>
Schema: [SchemaDefinition]
- <SchemaDefinition>
OperationTypes: [OperationTypeDefinition]
- <OperationTypeDefinition>
Operation: Operation("query")
Type: "Query"
- name: with comments and description
input: |
# before description comment
"description"
# after description comment
schema {
# before field comment
query: Query
# after field comment
}
ast: |
<SchemaDocument>
Schema: [SchemaDefinition]
- <SchemaDefinition>
Description: "description"
OperationTypes: [OperationTypeDefinition]
- <OperationTypeDefinition>
Operation: Operation("query")
Type: "Query"
Comment: "# before field comment\n"
BeforeDescriptionComment: "# before description comment\n"
AfterDescriptionComment: "# after description comment\n"
EndOfDefinitionComment: "# after field comment\n"
schema extensions:
- name: simple
input: |
extend schema {
mutation: Mutation
}
ast: |
<SchemaDocument>
SchemaExtension: [SchemaDefinition]
- <SchemaDefinition>
OperationTypes: [OperationTypeDefinition]
- <OperationTypeDefinition>
Operation: Operation("mutation")
Type: "Mutation"
- name: with comment and description
input: |
# before extend comment
extend schema {
# before field comment
mutation: Mutation
# after field comment
}
ast: |
<SchemaDocument>
SchemaExtension: [SchemaDefinition]
- <SchemaDefinition>
OperationTypes: [OperationTypeDefinition]
- <OperationTypeDefinition>
Operation: Operation("mutation")
Type: "Mutation"
Comment: "# before field comment\n"
AfterDescriptionComment: "# before extend comment\n"
EndOfDefinitionComment: "# after field comment\n"
- name: directive only
input: "extend schema @directive"
ast: |
<SchemaDocument>
SchemaExtension: [SchemaDefinition]
- <SchemaDefinition>
Directives: [Directive]
- <Directive>
Name: "directive"
- name: without anything errors
input: "extend schema"
error:
message: "Unexpected <EOF>"
locations: [{ line: 1, column: 14}]
inheritance:
- name: single
input: "type Hello implements World { field: String }"
ast: |
<SchemaDocument>
Definitions: [Definition]
- <Definition>
Kind: DefinitionKind("OBJECT")
Name: "Hello"
Interfaces: [string]
- "World"
Fields: [FieldDefinition]
- <FieldDefinition>
Name: "field"
Type: String
- name: multi
input: "type Hello implements Wo & rld { field: String }"
ast: |
<SchemaDocument>
Definitions: [Definition]
- <Definition>
Kind: DefinitionKind("OBJECT")
Name: "Hello"
Interfaces: [string]
- "Wo"
- "rld"
Fields: [FieldDefinition]
- <FieldDefinition>
Name: "field"
Type: String
- name: multi with leading amp
input: "type Hello implements & Wo & rld { field: String }"
ast: |
<SchemaDocument>
Definitions: [Definition]
- <Definition>
Kind: DefinitionKind("OBJECT")
Name: "Hello"
Interfaces: [string]
- "Wo"
- "rld"
Fields: [FieldDefinition]
- <FieldDefinition>
Name: "field"
Type: String
enums:
- name: single value
input: "enum Hello { WORLD }"
ast: |
<SchemaDocument>
Definitions: [Definition]
- <Definition>
Kind: DefinitionKind("ENUM")
Name: "Hello"
EnumValues: [EnumValueDefinition]
- <EnumValueDefinition>
Name: "WORLD"
- name: double value
input: "enum Hello { WO, RLD }"
ast: |
<SchemaDocument>
Definitions: [Definition]
- <Definition>
Kind: DefinitionKind("ENUM")
Name: "Hello"
EnumValues: [EnumValueDefinition]
- <EnumValueDefinition>
Name: "WO"
- <EnumValueDefinition>
Name: "RLD"
- name: must define one or more unique enum values
input: |
enum Hello {}
error:
message: "expected at least one definition, found }"
locations: [{ line: 1, column: 13 }]
interface:
- name: simple
input: |
interface Hello {
world: String
}
ast: |
<SchemaDocument>
Definitions: [Definition]
- <Definition>
Kind: DefinitionKind("INTERFACE")
Name: "Hello"
Fields: [FieldDefinition]
- <FieldDefinition>
Name: "world"
Type: String
- name: must define one or more fields
input: |
interface Hello {}
error:
message: "expected at least one definition, found }"
locations: [{ line: 1, column: 18 }]
- name: may define intermediate interfaces
input: |
interface IA {
id: ID!
}
interface IIA implements IA {
id: ID!
}
type A implements IIA {
id: ID!
}
ast: |
<SchemaDocument>
Definitions: [Definition]
- <Definition>
Kind: DefinitionKind("INTERFACE")
Name: "IA"
Fields: [FieldDefinition]
- <FieldDefinition>
Name: "id"
Type: ID!
- <Definition>
Kind: DefinitionKind("INTERFACE")
Name: "IIA"
Interfaces: [string]
- "IA"
Fields: [FieldDefinition]
- <FieldDefinition>
Name: "id"
Type: ID!
- <Definition>
Kind: DefinitionKind("OBJECT")
Name: "A"
Interfaces: [string]
- "IIA"
Fields: [FieldDefinition]
- <FieldDefinition>
Name: "id"
Type: ID!
unions:
- name: simple
input: "union Hello = World"
ast: |
<SchemaDocument>
Definitions: [Definition]
- <Definition>
Kind: DefinitionKind("UNION")
Name: "Hello"
Types: [string]
- "World"
- name: with two types
input: "union Hello = Wo | Rld"
ast: |
<SchemaDocument>
Definitions: [Definition]
- <Definition>
Kind: DefinitionKind("UNION")
Name: "Hello"
Types: [string]
- "Wo"
- "Rld"
- name: with leading pipe
input: "union Hello = | Wo | Rld"
ast: |
<SchemaDocument>
Definitions: [Definition]
- <Definition>
Kind: DefinitionKind("UNION")
Name: "Hello"
Types: [string]
- "Wo"
- "Rld"
- name: cant be empty
input: "union Hello = || Wo | Rld"
error:
message: "Expected Name, found |"
locations: [{ line: 1, column: 16 }]
- name: cant double pipe
input: "union Hello = Wo || Rld"
error:
message: "Expected Name, found |"
locations: [{ line: 1, column: 19 }]
- name: cant have trailing pipe
input: "union Hello = | Wo | Rld |"
error:
message: "Expected Name, found <EOF>"
locations: [{ line: 1, column: 27 }]
scalar:
- name: simple
input: "scalar Hello"
ast: |
<SchemaDocument>
Definitions: [Definition]
- <Definition>
Kind: DefinitionKind("SCALAR")
Name: "Hello"
input object:
- name: simple
input: |
input Hello {
world: String
}
ast: |
<SchemaDocument>
Definitions: [Definition]
- <Definition>
Kind: DefinitionKind("INPUT_OBJECT")
Name: "Hello"
Fields: [FieldDefinition]
- <FieldDefinition>
Name: "world"
Type: String
- name: can not have args
input: |
input Hello {
world(foo: Int): String
}
error:
message: "Expected :, found ("
locations: [{ line: 2, column: 8 }]
- name: must define one or more input fields
input: |
input Hello {}
error:
message: "expected at least one definition, found }"
locations: [{ line: 1, column: 14 }]
directives:
- name: simple
input: directive @foo on FIELD
ast: |
<SchemaDocument>
Directives: [DirectiveDefinition]
- <DirectiveDefinition>
Name: "foo"
Locations: [DirectiveLocation]
- DirectiveLocation("FIELD")
IsRepeatable: false
- name: executable
input: |
directive @onQuery on QUERY
directive @onMutation on MUTATION
directive @onSubscription on SUBSCRIPTION
directive @onField on FIELD
directive @onFragmentDefinition on FRAGMENT_DEFINITION
directive @onFragmentSpread on FRAGMENT_SPREAD
directive @onInlineFragment on INLINE_FRAGMENT
directive @onVariableDefinition on VARIABLE_DEFINITION
ast: |
<SchemaDocument>
Directives: [DirectiveDefinition]
- <DirectiveDefinition>
Name: "onQuery"
Locations: [DirectiveLocation]
- DirectiveLocation("QUERY")
IsRepeatable: false
- <DirectiveDefinition>
Name: "onMutation"
Locations: [DirectiveLocation]
- DirectiveLocation("MUTATION")
IsRepeatable: false
- <DirectiveDefinition>
Name: "onSubscription"
Locations: [DirectiveLocation]
- DirectiveLocation("SUBSCRIPTION")
IsRepeatable: false
- <DirectiveDefinition>
Name: "onField"
Locations: [DirectiveLocation]
- DirectiveLocation("FIELD")
IsRepeatable: false
- <DirectiveDefinition>
Name: "onFragmentDefinition"
Locations: [DirectiveLocation]
- DirectiveLocation("FRAGMENT_DEFINITION")
IsRepeatable: false
- <DirectiveDefinition>
Name: "onFragmentSpread"
Locations: [DirectiveLocation]
- DirectiveLocation("FRAGMENT_SPREAD")
IsRepeatable: false
- <DirectiveDefinition>
Name: "onInlineFragment"
Locations: [DirectiveLocation]
- DirectiveLocation("INLINE_FRAGMENT")
IsRepeatable: false
- <DirectiveDefinition>
Name: "onVariableDefinition"
Locations: [DirectiveLocation]
- DirectiveLocation("VARIABLE_DEFINITION")
IsRepeatable: false
- name: repeatable
input: directive @foo repeatable on FIELD
ast: |
<SchemaDocument>
Directives: [DirectiveDefinition]
- <DirectiveDefinition>
Name: "foo"
Locations: [DirectiveLocation]
- DirectiveLocation("FIELD")
IsRepeatable: true
- name: invalid location
input: "directive @foo on FIELD | INCORRECT_LOCATION"
error:
message: 'Unexpected Name "INCORRECT_LOCATION"'
locations: [{ line: 1, column: 27 }]
fuzzer:
- name: 1
input: "type o{d(g:["
error:
message: 'Expected Name, found <EOF>'
locations: [{ line: 1, column: 13 }]
- name: 2
input: "\"\"\"\r"
error:
message: 'Unexpected <Invalid>'
locations: [{ line: 2, column: 1 }]