Stash box client interface (#751)

* Add gql client generation files
* Update dependencies
* Add stash-box client generation to the makefile
* Move scraped scene object matchers to models
* Add stash-box to scrape with dropdown
* Add scrape scene from fingerprint in UI
This commit is contained in:
WithoutPants
2020-09-17 19:57:18 +10:00
committed by GitHub
parent b0b5621337
commit 7a45943e8e
324 changed files with 34978 additions and 17323 deletions

136
vendor/github.com/vektah/gqlparser/v2/parser/parser.go generated vendored Normal file
View File

@@ -0,0 +1,136 @@
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 *gqlerror.Error
peeked bool
peekToken lexer.Token
peekError *gqlerror.Error
prev lexer.Token
}
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
}
return p.peekToken
}
func (p *parser) error(tok lexer.Token, format string, args ...interface{}) {
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
}
if p.peeked {
p.peeked = false
p.prev, p.err = p.peekToken, p.peekError
} else {
p.prev, p.err = p.lexer.ReadToken()
}
return p.prev
}
func (p *parser) expectKeyword(value string) lexer.Token {
tok := p.peek()
if tok.Kind == lexer.Name && tok.Value == value {
return p.next()
}
p.error(tok, "Expected %s, found %s", strconv.Quote(value), tok.String())
return tok
}
func (p *parser) expect(kind lexer.Type) lexer.Token {
tok := p.peek()
if tok.Kind == kind {
return p.next()
}
p.error(tok, "Expected %s, found %s", kind, tok.Kind.String())
return tok
}
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 lexer.Type, 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 lexer.Type, end lexer.Type, cb func()) {
hasDef := p.skip(start)
if !hasDef {
return
}
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
}
p.next()
}

348
vendor/github.com/vektah/gqlparser/v2/parser/query.go generated vendored Normal file
View File

@@ -0,0 +1,348 @@
package parser
import (
"github.com/vektah/gqlparser/v2/gqlerror"
"github.com/vektah/gqlparser/v2/lexer"
. "github.com/vektah/gqlparser/v2/ast"
)
func ParseQuery(source *Source) (*QueryDocument, *gqlerror.Error) {
p := parser{
lexer: lexer.New(source),
}
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(),
Operation: Query,
SelectionSet: p.parseRequiredSelectionSet(),
}
}
var od OperationDefinition
od.Position = p.peekPos()
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.many(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.Variable = p.parseVariable()
p.expect(lexer.Colon)
def.Type = p.parseTypeReference()
if p.skip(lexer.Equals) {
def.DefaultValue = p.parseValueLiteral(true)
}
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 SelectionSet(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 SelectionSet(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.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.many(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.Name = p.parseName()
p.expect(lexer.Colon)
arg.Value = p.parseValueLiteral(isConst)
return &arg
}
func (p *parser) parseFragment() Selection {
p.expect(lexer.Spread)
if peek := p.peek(); peek.Kind == lexer.Name && peek.Value != "on" {
return &FragmentSpread{
Position: p.peekPos(),
Name: p.parseFragmentName(),
Directives: p.parseDirectives(false),
}
}
var def InlineFragment
def.Position = p.peekPos()
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()
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, 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, Raw: token.Value, Kind: kind}
}
func (p *parser) parseList(isConst bool) *Value {
var values ChildValueList
pos := p.peekPos()
p.many(lexer.BracketL, lexer.BracketR, func() {
values = append(values, &ChildValue{Value: p.parseValueLiteral(isConst)})
})
return &Value{Children: values, Kind: ListValue, Position: pos}
}
func (p *parser) parseObject(isConst bool) *Value {
var fields ChildValueList
pos := p.peekPos()
p.many(lexer.BraceL, lexer.BraceR, func() {
fields = append(fields, p.parseObjectField(isConst))
})
return &Value{Children: fields, Kind: ObjectValue, Position: pos}
}
func (p *parser) parseObjectField(isConst bool) *ChildValue {
field := ChildValue{}
field.Position = p.peekPos()
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.Position = p.peekPos()
typ.NonNull = true
}
return &typ
}
func (p *parser) parseName() string {
token := p.expect(lexer.Name)
return token.Value
}

View File

@@ -0,0 +1,520 @@
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 }]
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"
- <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 }]

527
vendor/github.com/vektah/gqlparser/v2/parser/schema.go generated vendored Normal file
View File

@@ -0,0 +1,527 @@
package parser
import (
. "github.com/vektah/gqlparser/v2/ast"
"github.com/vektah/gqlparser/v2/gqlerror"
"github.com/vektah/gqlparser/v2/lexer"
)
func ParseSchema(source *Source) (*SchemaDocument, *gqlerror.Error) {
p := parser{
lexer: lexer.New(source),
}
ast, err := p.parseSchemaDocument(), p.err
if err != nil {
return nil, err
}
for _, def := range ast.Definitions {
def.BuiltIn = source.BuiltIn
}
for _, def := range ast.Extensions {
def.BuiltIn = source.BuiltIn
}
return ast, nil
}
func ParseSchemas(inputs ...*Source) (*SchemaDocument, *gqlerror.Error) {
ast := &SchemaDocument{}
for _, input := range inputs {
inputAst, err := ParseSchema(input)
if err != nil {
return nil, err
}
ast.Merge(inputAst)
}
return ast, 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 string
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 != "" {
p.unexpectedToken(p.prev)
}
p.parseTypeSystemExtension(&doc)
default:
p.unexpectedError()
return nil
}
}
return &doc
}
func (p *parser) parseDescription() string {
token := p.peek()
if token.Kind != lexer.BlockString && token.Kind != lexer.String {
return ""
}
return p.next().Value
}
func (p *parser) parseTypeSystemDefinition(description string) *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 string) *SchemaDefinition {
p.expectKeyword("schema")
def := SchemaDefinition{Description: description}
def.Position = p.peekPos()
def.Description = description
def.Directives = p.parseDirectives(true)
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.Operation = p.parseOperationType()
p.expect(lexer.Colon)
op.Type = p.parseName()
return &op
}
func (p *parser) parseScalarTypeDefinition(description string) *Definition {
p.expectKeyword("scalar")
var def Definition
def.Position = p.peekPos()
def.Kind = Scalar
def.Description = description
def.Name = p.parseName()
def.Directives = p.parseDirectives(true)
return &def
}
func (p *parser) parseObjectTypeDefinition(description string) *Definition {
p.expectKeyword("type")
var def Definition
def.Position = p.peekPos()
def.Kind = Object
def.Description = description
def.Name = p.parseName()
def.Interfaces = p.parseImplementsInterfaces()
def.Directives = p.parseDirectives(true)
def.Fields = 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 {
var defs FieldList
p.some(lexer.BraceL, lexer.BraceR, func() {
defs = append(defs, p.parseFieldDefinition())
})
return defs
}
func (p *parser) parseFieldDefinition() *FieldDefinition {
var def FieldDefinition
def.Position = p.peekPos()
def.Description = p.parseDescription()
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()
def.Description = p.parseDescription()
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()
def.Description = p.parseDescription()
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 string) *Definition {
p.expectKeyword("interface")
var def Definition
def.Position = p.peekPos()
def.Kind = Interface
def.Description = description
def.Name = p.parseName()
def.Directives = p.parseDirectives(true)
def.Fields = p.parseFieldsDefinition()
return &def
}
func (p *parser) parseUnionTypeDefinition(description string) *Definition {
p.expectKeyword("union")
var def Definition
def.Position = p.peekPos()
def.Kind = Union
def.Description = description
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 string) *Definition {
p.expectKeyword("enum")
var def Definition
def.Position = p.peekPos()
def.Kind = Enum
def.Description = description
def.Name = p.parseName()
def.Directives = p.parseDirectives(true)
def.EnumValues = p.parseEnumValuesDefinition()
return &def
}
func (p *parser) parseEnumValuesDefinition() EnumValueList {
var values EnumValueList
p.some(lexer.BraceL, lexer.BraceR, func() {
values = append(values, p.parseEnumValueDefinition())
})
return values
}
func (p *parser) parseEnumValueDefinition() *EnumValueDefinition {
return &EnumValueDefinition{
Position: p.peekPos(),
Description: p.parseDescription(),
Name: p.parseName(),
Directives: p.parseDirectives(true),
}
}
func (p *parser) parseInputObjectTypeDefinition(description string) *Definition {
p.expectKeyword("input")
var def Definition
def.Position = p.peekPos()
def.Kind = InputObject
def.Description = description
def.Name = p.parseName()
def.Directives = p.parseDirectives(true)
def.Fields = p.parseInputFieldsDefinition()
return &def
}
func (p *parser) parseInputFieldsDefinition() FieldList {
var values FieldList
p.some(lexer.BraceL, lexer.BraceR, func() {
values = append(values, p.parseInputValueDef())
})
return values
}
func (p *parser) parseTypeSystemExtension(doc *SchemaDocument) {
p.expectKeyword("extend")
switch p.peek().Value {
case "schema":
doc.SchemaExtension = append(doc.SchemaExtension, p.parseSchemaExtension())
case "scalar":
doc.Extensions = append(doc.Extensions, p.parseScalarTypeExtension())
case "type":
doc.Extensions = append(doc.Extensions, p.parseObjectTypeExtension())
case "interface":
doc.Extensions = append(doc.Extensions, p.parseInterfaceTypeExtension())
case "union":
doc.Extensions = append(doc.Extensions, p.parseUnionTypeExtension())
case "enum":
doc.Extensions = append(doc.Extensions, p.parseEnumTypeExtension())
case "input":
doc.Extensions = append(doc.Extensions, p.parseInputObjectTypeExtension())
default:
p.unexpectedError()
}
}
func (p *parser) parseSchemaExtension() *SchemaDefinition {
p.expectKeyword("schema")
var def SchemaDefinition
def.Position = p.peekPos()
def.Directives = p.parseDirectives(true)
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() *Definition {
p.expectKeyword("scalar")
var def Definition
def.Position = p.peekPos()
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() *Definition {
p.expectKeyword("type")
var def Definition
def.Position = p.peekPos()
def.Kind = Object
def.Name = p.parseName()
def.Interfaces = p.parseImplementsInterfaces()
def.Directives = p.parseDirectives(true)
def.Fields = p.parseFieldsDefinition()
if len(def.Interfaces) == 0 && len(def.Directives) == 0 && len(def.Fields) == 0 {
p.unexpectedError()
}
return &def
}
func (p *parser) parseInterfaceTypeExtension() *Definition {
p.expectKeyword("interface")
var def Definition
def.Position = p.peekPos()
def.Kind = Interface
def.Name = p.parseName()
def.Directives = p.parseDirectives(true)
def.Fields = p.parseFieldsDefinition()
if len(def.Directives) == 0 && len(def.Fields) == 0 {
p.unexpectedError()
}
return &def
}
func (p *parser) parseUnionTypeExtension() *Definition {
p.expectKeyword("union")
var def Definition
def.Position = p.peekPos()
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() *Definition {
p.expectKeyword("enum")
var def Definition
def.Position = p.peekPos()
def.Kind = Enum
def.Name = p.parseName()
def.Directives = p.parseDirectives(true)
def.EnumValues = p.parseEnumValuesDefinition()
if len(def.Directives) == 0 && len(def.EnumValues) == 0 {
p.unexpectedError()
}
return &def
}
func (p *parser) parseInputObjectTypeExtension() *Definition {
p.expectKeyword("input")
var def Definition
def.Position = p.peekPos()
def.Kind = InputObject
def.Name = p.parseName()
def.Directives = p.parseDirectives(false)
def.Fields = p.parseInputFieldsDefinition()
if len(def.Directives) == 0 && len(def.Fields) == 0 {
p.unexpectedError()
}
return &def
}
func (p *parser) parseDirectiveDefinition(description string) *DirectiveDefinition {
p.expectKeyword("directive")
p.expect(lexer.At)
var def DirectiveDefinition
def.Position = p.peekPos()
def.Description = description
def.Name = p.parseName()
def.Arguments = p.parseArgumentDefs()
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 `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 ""
}

View File

@@ -0,0 +1,540 @@
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 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: |
"""
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
- 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: |
extend type Hello {
world: String
}
ast: |
<SchemaDocument>
Extensions: [Definition]
- <Definition>
Kind: DefinitionKind("OBJECT")
Name: "Hello"
Fields: [FieldDefinition]
- <FieldDefinition>
Name: "world"
Type: String
- 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 }]
schema definition:
- name: simple
input: |
schema {
query: Query
}
ast: |
<SchemaDocument>
Schema: [SchemaDefinition]
- <SchemaDefinition>
OperationTypes: [OperationTypeDefinition]
- <OperationTypeDefinition>
Operation: Operation("query")
Type: "Query"
schema extensions:
- name: simple
input: |
extend schema {
mutation: Mutation
}
ast: |
<SchemaDocument>
SchemaExtension: [SchemaDefinition]
- <SchemaDefinition>
OperationTypes: [OperationTypeDefinition]
- <OperationTypeDefinition>
Operation: Operation("mutation")
Type: "Mutation"
- 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}]
type extensions:
- 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"
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 }]
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")
- 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: 1, column: 5 }]