1.0.0
This commit is contained in:
parent
fa624e48df
commit
a059010428
13 changed files with 391 additions and 26 deletions
|
@ -2,9 +2,9 @@
|
||||||
[![Pub](https://img.shields.io/pub/v/graphql_parser.svg)](https://pub.dartlang.org/packages/graphql_parser)
|
[![Pub](https://img.shields.io/pub/v/graphql_parser.svg)](https://pub.dartlang.org/packages/graphql_parser)
|
||||||
[![build status](https://travis-ci.org/thosakwe/graphql_parser.svg)](https://travis-ci.org/thosakwe/graphql_parser)
|
[![build status](https://travis-ci.org/thosakwe/graphql_parser.svg)](https://travis-ci.org/thosakwe/graphql_parser)
|
||||||
|
|
||||||
Parses GraphQL queries and schemas.
|
Parses GraphQL queries and schemas. Also includes a `GraphQLVisitor` class.
|
||||||
|
|
||||||
*This library is merely a parser*. Any sort of actual GraphQL API functionality must be implemented by you,
|
*This library is merely a parser/visitor*. Any sort of actual GraphQL API functionality must be implemented by you,
|
||||||
or by a third-party package.
|
or by a third-party package.
|
||||||
|
|
||||||
[Angel framework](https://angel-dart.github.io)
|
[Angel framework](https://angel-dart.github.io)
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import 'dart:async';
|
import 'package:graphql_parser/graphql_parser.dart';
|
||||||
import 'package:graphql_parser/src/language/language.dart';
|
|
||||||
|
|
||||||
final String INPUT = '''
|
final String INPUT = '''
|
||||||
{
|
{
|
||||||
|
@ -13,4 +12,15 @@ final String INPUT = '''
|
||||||
main() {
|
main() {
|
||||||
var tokens = scan(INPUT);
|
var tokens = scan(INPUT);
|
||||||
var parser = new Parser(tokens);
|
var parser = new Parser(tokens);
|
||||||
|
var doc = parser.parseDocument();
|
||||||
|
|
||||||
|
var operation = doc.definitions.first as OperationDefinitionContext;
|
||||||
|
|
||||||
|
var projectField = operation.selectionSet.selections.first.field;
|
||||||
|
print(projectField.fieldName.name); // project
|
||||||
|
print(projectField.arguments.first.name); // name
|
||||||
|
print(projectField.arguments.first.valueOrVariable.value.value); // GraphQL
|
||||||
|
|
||||||
|
var taglineField = projectField.selectionSet.selections.first.field;
|
||||||
|
print(taglineField.fieldName.name); // tagline
|
||||||
}
|
}
|
||||||
|
|
50
example/visitor.dart
Normal file
50
example/visitor.dart
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
import 'package:graphql_parser/graphql_parser.dart';
|
||||||
|
import 'package:graphql_parser/visitor.dart';
|
||||||
|
|
||||||
|
const String QUERY = '''
|
||||||
|
{
|
||||||
|
foo,
|
||||||
|
baz: bar
|
||||||
|
}
|
||||||
|
''';
|
||||||
|
|
||||||
|
const Map<String, dynamic> DATA = const {
|
||||||
|
'foo': 'hello',
|
||||||
|
'bar': 'world',
|
||||||
|
'quux': 'extraneous'
|
||||||
|
};
|
||||||
|
|
||||||
|
main() {
|
||||||
|
// Highly-simplified querying example...
|
||||||
|
var result = new MapQuerier(DATA).execute(QUERY);
|
||||||
|
print(result); // { foo: hello, baz: world }
|
||||||
|
print(result['foo']); // hello
|
||||||
|
print(result['baz']); // world
|
||||||
|
}
|
||||||
|
|
||||||
|
class MapQuerier extends GraphQLVisitor {
|
||||||
|
final Map<String, dynamic> data;
|
||||||
|
final Map<String, dynamic> result = {};
|
||||||
|
|
||||||
|
MapQuerier(this.data);
|
||||||
|
|
||||||
|
Map<String, dynamic> execute(String query) {
|
||||||
|
var doc = new Parser(scan(query)).parseDocument();
|
||||||
|
visitDocument(doc);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
visitField(FieldContext ctx) {
|
||||||
|
String realName, alias;
|
||||||
|
if (ctx.fieldName.alias == null)
|
||||||
|
realName = alias = ctx.fieldName.name;
|
||||||
|
else {
|
||||||
|
realName = ctx.fieldName.alias.name;
|
||||||
|
alias = ctx.fieldName.alias.alias;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set output field...
|
||||||
|
result[alias] = data[realName];
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,15 +7,14 @@ class AliasContext extends Node {
|
||||||
|
|
||||||
AliasContext(this.NAME1, this.COLON, this.NAME2);
|
AliasContext(this.NAME1, this.COLON, this.NAME2);
|
||||||
|
|
||||||
/// The actual name of the value.
|
|
||||||
String get name => NAME1.text;
|
|
||||||
|
|
||||||
/// The aliased name of the value.
|
/// The aliased name of the value.
|
||||||
String get alias => NAME2.text;
|
String get alias => NAME1.text;
|
||||||
|
|
||||||
|
/// The actual name of the value.
|
||||||
|
String get name => NAME2.text;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
SourceSpan get span =>
|
SourceSpan get span => NAME1.span.union(COLON.span).union(NAME2.span);
|
||||||
new SourceSpan(NAME1.span?.start, NAME2.span?.end, toSource());
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toSource() => '${NAME1.text}:${NAME2.text}';
|
String toSource() => '${NAME1.text}:${NAME2.text}';
|
||||||
|
|
|
@ -7,7 +7,4 @@ abstract class Node {
|
||||||
SourceLocation get end => span.end;
|
SourceLocation get end => span.end;
|
||||||
|
|
||||||
String toSource();
|
String toSource();
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() => '${runtimeType}: ${toSource()}';
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,10 +11,10 @@ class OperationDefinitionContext extends DefinitionContext {
|
||||||
final List<DirectiveContext> directives = [];
|
final List<DirectiveContext> directives = [];
|
||||||
final SelectionSetContext selectionSet;
|
final SelectionSetContext selectionSet;
|
||||||
|
|
||||||
bool get isMutation => TYPE.text == 'mutation';
|
bool get isMutation => TYPE?.text == 'mutation';
|
||||||
bool get isQuery => TYPE.text == 'query';
|
bool get isQuery => TYPE?.text == 'query';
|
||||||
|
|
||||||
String get name => NAME.text;
|
String get name => NAME?.text;
|
||||||
|
|
||||||
OperationDefinitionContext(
|
OperationDefinitionContext(
|
||||||
this.TYPE, this.NAME, this.variableDefinitions, this.selectionSet) {
|
this.TYPE, this.NAME, this.variableDefinitions, this.selectionSet) {
|
||||||
|
|
|
@ -49,9 +49,69 @@ class Parser {
|
||||||
DefinitionContext parseDefinition() =>
|
DefinitionContext parseDefinition() =>
|
||||||
parseOperationDefinition() ?? parseFragmentDefinition();
|
parseOperationDefinition() ?? parseFragmentDefinition();
|
||||||
|
|
||||||
OperationDefinitionContext parseOperationDefinition() {}
|
OperationDefinitionContext parseOperationDefinition() {
|
||||||
|
var selectionSet = parseSelectionSet();
|
||||||
|
if (selectionSet != null)
|
||||||
|
return new OperationDefinitionContext(null, null, null, selectionSet);
|
||||||
|
else {
|
||||||
|
if (next(TokenType.MUTATION) || next(TokenType.QUERY)) {
|
||||||
|
var TYPE = current;
|
||||||
|
if (next(TokenType.NAME)) {
|
||||||
|
var NAME = current;
|
||||||
|
var variables = parseVariableDefinitions();
|
||||||
|
var dirs = parseDirectives();
|
||||||
|
var selectionSet = parseSelectionSet();
|
||||||
|
if (selectionSet != null)
|
||||||
|
return new OperationDefinitionContext(
|
||||||
|
TYPE, NAME, variables, selectionSet)
|
||||||
|
..directives.addAll(dirs);
|
||||||
|
else
|
||||||
|
throw new SyntaxError.fromSourceLocation(
|
||||||
|
'Expected selection set in fragment definition.',
|
||||||
|
NAME.span.end);
|
||||||
|
} else
|
||||||
|
throw new SyntaxError.fromSourceLocation(
|
||||||
|
'Expected name after operation type "${TYPE.text}" in operation definition.',
|
||||||
|
TYPE.span.end);
|
||||||
|
} else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
FragmentDefinitionContext parseFragmentDefinition() {}
|
FragmentDefinitionContext parseFragmentDefinition() {
|
||||||
|
if (next(TokenType.FRAGMENT)) {
|
||||||
|
var FRAGMENT = current;
|
||||||
|
if (next(TokenType.NAME)) {
|
||||||
|
var NAME = current;
|
||||||
|
if (next(TokenType.ON)) {
|
||||||
|
var ON = current;
|
||||||
|
var typeCondition = parseTypeCondition();
|
||||||
|
if (typeCondition != null) {
|
||||||
|
var dirs = parseDirectives();
|
||||||
|
var selectionSet = parseSelectionSet();
|
||||||
|
if (selectionSet != null)
|
||||||
|
return new FragmentDefinitionContext(
|
||||||
|
FRAGMENT, NAME, ON, typeCondition, selectionSet)
|
||||||
|
..directives.addAll(dirs);
|
||||||
|
else
|
||||||
|
throw new SyntaxError.fromSourceLocation(
|
||||||
|
'Expected selection set in fragment definition.',
|
||||||
|
typeCondition.span.end);
|
||||||
|
} else
|
||||||
|
throw new SyntaxError.fromSourceLocation(
|
||||||
|
'Expected type condition after "on" in fragment definition.',
|
||||||
|
ON.span.end);
|
||||||
|
} else
|
||||||
|
throw new SyntaxError.fromSourceLocation(
|
||||||
|
'Expected "on" after name "${NAME.text}" in fragment definition.',
|
||||||
|
NAME.span.end);
|
||||||
|
} else
|
||||||
|
throw new SyntaxError.fromSourceLocation(
|
||||||
|
'Expected name after "fragment" in fragment definition.',
|
||||||
|
FRAGMENT.span.end);
|
||||||
|
} else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
FragmentSpreadContext parseFragmentSpread() {
|
FragmentSpreadContext parseFragmentSpread() {
|
||||||
if (next(TokenType.ELLIPSIS)) {
|
if (next(TokenType.ELLIPSIS)) {
|
||||||
|
@ -164,7 +224,27 @@ class Parser {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
VariableDefinitionsContext parseVariableDefinitions() {}
|
VariableDefinitionsContext parseVariableDefinitions() {
|
||||||
|
if (next(TokenType.LPAREN)) {
|
||||||
|
var LPAREN = current;
|
||||||
|
List<VariableDefinitionContext> defs = [];
|
||||||
|
VariableDefinitionContext def = parseVariableDefinition();
|
||||||
|
|
||||||
|
while (def != null) {
|
||||||
|
defs.add(def);
|
||||||
|
maybe(TokenType.COMMA);
|
||||||
|
def = parseVariableDefinition();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (next(TokenType.RPAREN))
|
||||||
|
return new VariableDefinitionsContext(LPAREN, current)
|
||||||
|
..variableDefinitions.addAll(defs);
|
||||||
|
else
|
||||||
|
throw new SyntaxError.fromSourceLocation(
|
||||||
|
'Expected ")" after variable definitions.', LPAREN.span.end);
|
||||||
|
} else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
VariableDefinitionContext parseVariableDefinition() {
|
VariableDefinitionContext parseVariableDefinition() {
|
||||||
var variable = parseVariable();
|
var variable = parseVariable();
|
||||||
|
|
125
lib/visitor.dart
Normal file
125
lib/visitor.dart
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
import 'graphql_parser.dart';
|
||||||
|
|
||||||
|
class GraphQLVisitor {
|
||||||
|
visitDocument(DocumentContext ctx) {
|
||||||
|
ctx.definitions.forEach(visitDefinition);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitDefinition(DefinitionContext ctx) {
|
||||||
|
if (ctx is OperationDefinitionContext)
|
||||||
|
visitOperationDefinition(ctx);
|
||||||
|
else if (ctx is FragmentDefinitionContext) visitFragmentDefinition(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitOperationDefinition(OperationDefinitionContext ctx) {
|
||||||
|
if (ctx.variableDefinitions != null)
|
||||||
|
visitVariableDefinitions(ctx.variableDefinitions);
|
||||||
|
ctx.directives.forEach(visitDirective);
|
||||||
|
visitSelectionSet(ctx.selectionSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitFragmentDefinition(FragmentDefinitionContext ctx) {
|
||||||
|
visitTypeCondition(ctx.typeCondition);
|
||||||
|
ctx.directives.forEach(visitDirective);
|
||||||
|
visitSelectionSet(ctx.selectionSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitSelectionSet(SelectionSetContext ctx) {
|
||||||
|
ctx.selections.forEach(visitSelection);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitSelection(SelectionContext ctx) {
|
||||||
|
if (ctx.field != null) visitField(ctx.field);
|
||||||
|
if (ctx.fragmentSpread != null) visitFragmentSpread(ctx.fragmentSpread);
|
||||||
|
if (ctx.inlineFragment != null) visitInlineFragment(ctx.inlineFragment);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitInlineFragment(InlineFragmentContext ctx) {
|
||||||
|
visitTypeCondition(ctx.typeCondition);
|
||||||
|
ctx.directives.forEach(visitDirective);
|
||||||
|
visitSelectionSet(ctx.selectionSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitFragmentSpread(FragmentSpreadContext ctx) {
|
||||||
|
ctx.directives.forEach(visitDirective);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitField(FieldContext ctx) {
|
||||||
|
visitFieldName(ctx.fieldName);
|
||||||
|
ctx.arguments.forEach(visitArgument);
|
||||||
|
ctx.directives.forEach(visitDirective);
|
||||||
|
if (ctx.selectionSet != null) ;
|
||||||
|
visitSelectionSet(ctx.selectionSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitFieldName(FieldNameContext ctx) {
|
||||||
|
if (ctx.alias != null) visitAlias(ctx.alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitAlias(AliasContext ctx) {}
|
||||||
|
|
||||||
|
visitDirective(DirectiveContext ctx) {
|
||||||
|
if (ctx.valueOrVariable != null) visitValueOrVariable(ctx.valueOrVariable);
|
||||||
|
if (ctx.argument != null) visitArgument(ctx.argument);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitArgument(ArgumentContext ctx) {
|
||||||
|
visitValueOrVariable(ctx.valueOrVariable);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitVariableDefinitions(VariableDefinitionsContext ctx) {
|
||||||
|
ctx.variableDefinitions.forEach(visitVariableDefinition);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitVariableDefinition(VariableDefinitionContext ctx) {
|
||||||
|
visitVariable(ctx.variable);
|
||||||
|
visitType(ctx.type);
|
||||||
|
if (ctx.defaultValue != null) visitDefaultValue(ctx.defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitVariable(VariableContext ctx) {}
|
||||||
|
|
||||||
|
visitValueOrVariable(ValueOrVariableContext ctx) {
|
||||||
|
if (ctx.variable != null) visitVariable(ctx.variable);
|
||||||
|
if (ctx.value != null) visitValue(ctx.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitDefaultValue(DefaultValueContext ctx) {
|
||||||
|
visitValue(ctx.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitValue(ValueContext ctx) {
|
||||||
|
if (ctx is StringValueContext)
|
||||||
|
visitStringValue(ctx);
|
||||||
|
else if (ctx is NumberValueContext)
|
||||||
|
visitNumberValue(ctx);
|
||||||
|
else if (ctx is BooleanValueContext)
|
||||||
|
visitBooleanValue(ctx);
|
||||||
|
else if (ctx is ArrayValueContext) visitArrayValue(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitStringValue(StringValueContext ctx) {}
|
||||||
|
|
||||||
|
visitBooleanValue(BooleanValueContext ctx) {}
|
||||||
|
|
||||||
|
visitNumberValue(NumberValueContext ctx) {}
|
||||||
|
|
||||||
|
visitArrayValue(ArrayValueContext ctx) {
|
||||||
|
ctx.values.forEach(visitValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitTypeCondition(TypeConditionContext ctx) {
|
||||||
|
visitTypeName(ctx.typeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitType(TypeContext ctx) {
|
||||||
|
if (ctx.typeName != null) visitTypeName(ctx.typeName);
|
||||||
|
if (ctx.listType != null) visitListType(ctx.listType);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitListType(ListTypeContext ctx) {
|
||||||
|
visitType(ctx.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitTypeName(TypeNameContext ctx) {}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
name: graphql_parser
|
name: graphql_parser
|
||||||
version: 0.0.0
|
version: 1.0.0
|
||||||
description: Parses GraphQL queries and schemas.
|
description: Parses GraphQL queries and schemas.
|
||||||
author: Tobe O <thosakwe@gmail.com>
|
author: Tobe O <thosakwe@gmail.com>
|
||||||
homepage: https://github.com/thosakwe/graphql_parser
|
homepage: https://github.com/thosakwe/graphql_parser
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
import 'argument_test.dart' as argument;
|
import 'argument_test.dart' as argument;
|
||||||
import 'directive_test.dart' as directive;
|
import 'directive_test.dart' as directive;
|
||||||
|
import 'document_test.dart' as document;
|
||||||
import 'field_test.dart' as field;
|
import 'field_test.dart' as field;
|
||||||
import 'fragment_spread_test.dart' as fragment_spread;
|
import 'fragment_spread_test.dart' as fragment_spread;
|
||||||
import 'inline_fragment_test.dart' as inline_fragment;
|
import 'inline_fragment_test.dart' as inline_fragment;
|
||||||
|
@ -13,6 +14,7 @@ import 'variable_test.dart' as variable;
|
||||||
main() {
|
main() {
|
||||||
group('argument', argument.main);
|
group('argument', argument.main);
|
||||||
group('directive', directive.main);
|
group('directive', directive.main);
|
||||||
|
group('document', document.main);
|
||||||
group('field', field.main);
|
group('field', field.main);
|
||||||
group('fragment spread', fragment_spread.main);
|
group('fragment spread', fragment_spread.main);
|
||||||
group('inline fragment', inline_fragment.main);
|
group('inline fragment', inline_fragment.main);
|
||||||
|
|
100
test/document_test.dart
Normal file
100
test/document_test.dart
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
import 'package:graphql_parser/graphql_parser.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
import 'common.dart';
|
||||||
|
import 'directive_test.dart';
|
||||||
|
import 'field_test.dart';
|
||||||
|
import 'selection_set_test.dart';
|
||||||
|
import 'type_test.dart';
|
||||||
|
import 'value_test.dart';
|
||||||
|
import 'variable_definition_test.dart';
|
||||||
|
|
||||||
|
main() {
|
||||||
|
test('fragment', () {
|
||||||
|
var fragment = parse('''
|
||||||
|
fragment PostInfo on Post {
|
||||||
|
description
|
||||||
|
comments {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
''').parseFragmentDefinition();
|
||||||
|
|
||||||
|
expect(fragment, isNotNull);
|
||||||
|
expect(fragment.name, 'PostInfo');
|
||||||
|
expect(fragment.typeCondition.typeName.name, 'Post');
|
||||||
|
expect(
|
||||||
|
fragment.selectionSet,
|
||||||
|
isSelectionSet([
|
||||||
|
isField(fieldName: isFieldName('description')),
|
||||||
|
isField(
|
||||||
|
fieldName: isFieldName('comments'),
|
||||||
|
selectionSet:
|
||||||
|
isSelectionSet([isField(fieldName: isFieldName('id'))])),
|
||||||
|
]));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('fragment exceptions', () {
|
||||||
|
expect(
|
||||||
|
() => parse('fragment').parseFragmentDefinition(), throwsSyntaxError);
|
||||||
|
expect(() => parse('fragment foo').parseFragmentDefinition(),
|
||||||
|
throwsSyntaxError);
|
||||||
|
expect(() => parse('fragment foo on').parseFragmentDefinition(),
|
||||||
|
throwsSyntaxError);
|
||||||
|
expect(() => parse('fragment foo on bar').parseFragmentDefinition(),
|
||||||
|
throwsSyntaxError);
|
||||||
|
});
|
||||||
|
|
||||||
|
group('operation', () {
|
||||||
|
test('with selection set', () {
|
||||||
|
var op = parse('{foo, bar: baz}').parseOperationDefinition();
|
||||||
|
expect(op.variableDefinitions, isNull);
|
||||||
|
expect(op.isQuery, isFalse);
|
||||||
|
expect(op.isMutation, isFalse);
|
||||||
|
expect(op.name, isNull);
|
||||||
|
expect(
|
||||||
|
op.selectionSet,
|
||||||
|
isSelectionSet([
|
||||||
|
isField(fieldName: isFieldName('foo')),
|
||||||
|
isField(fieldName: isFieldName('bar', alias: 'baz'))
|
||||||
|
]));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('with operation type', () {
|
||||||
|
var doc =
|
||||||
|
parse(r'query foo ($one: [int] = 2) @foo @bar: 2 {foo, bar: baz}')
|
||||||
|
.parseDocument();
|
||||||
|
expect(doc.definitions, hasLength(1));
|
||||||
|
expect(doc.definitions.first,
|
||||||
|
const isInstanceOf<OperationDefinitionContext>());
|
||||||
|
var op = doc.definitions.first as OperationDefinitionContext;
|
||||||
|
expect(op.isMutation, isFalse);
|
||||||
|
expect(op.isQuery, isTrue);
|
||||||
|
|
||||||
|
expect(op.variableDefinitions.variableDefinitions, hasLength(1));
|
||||||
|
expect(
|
||||||
|
op.variableDefinitions.variableDefinitions.first,
|
||||||
|
isVariableDefinition('one',
|
||||||
|
type: isListType(isType('int'), isNullable: true),
|
||||||
|
defaultValue: isValue(2)));
|
||||||
|
|
||||||
|
expect(op.directives, hasLength(2));
|
||||||
|
expect(op.directives[0], isDirective('foo'));
|
||||||
|
expect(op.directives[1], isDirective('bar', valueOrVariable: equals(2)));
|
||||||
|
|
||||||
|
expect(op.selectionSet, isNotNull);
|
||||||
|
expect(
|
||||||
|
op.selectionSet,
|
||||||
|
isSelectionSet([
|
||||||
|
isField(fieldName: isFieldName('foo')),
|
||||||
|
isField(fieldName: isFieldName('bar', alias: 'baz'))
|
||||||
|
]));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('exceptions', () {
|
||||||
|
expect(
|
||||||
|
() => parse('query').parseOperationDefinition(), throwsSyntaxError);
|
||||||
|
expect(() => parse('query foo()').parseOperationDefinition(),
|
||||||
|
throwsSyntaxError);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -109,22 +109,22 @@ class _IsField extends Matcher {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _IsFieldName extends Matcher {
|
class _IsFieldName extends Matcher {
|
||||||
final String name, alias;
|
final String name, realName;
|
||||||
|
|
||||||
_IsFieldName(this.name, this.alias);
|
_IsFieldName(this.name, this.realName);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Description describe(Description description) {
|
Description describe(Description description) {
|
||||||
if (alias != null)
|
if (realName != null)
|
||||||
return description.add('is field with name "$name" and alias "$alias"');
|
return description.add('is field with name "$name" and alias "$realName"');
|
||||||
return description.add('is field with name "$name"');
|
return description.add('is field with name "$name"');
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool matches(item, Map matchState) {
|
bool matches(item, Map matchState) {
|
||||||
var fieldName = item is FieldNameContext ? item : parseFieldName(item);
|
var fieldName = item is FieldNameContext ? item : parseFieldName(item);
|
||||||
if (alias != null)
|
if (realName != null)
|
||||||
return fieldName.alias?.name == name && fieldName.alias?.alias == alias;
|
return fieldName.alias?.alias == name && fieldName.alias?.name == realName;
|
||||||
else
|
else
|
||||||
return fieldName.name == name;
|
return fieldName.name == name;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,8 @@ main() {
|
||||||
expect(() => parseVariableDefinition(r'$foo'), throwsSyntaxError);
|
expect(() => parseVariableDefinition(r'$foo'), throwsSyntaxError);
|
||||||
expect(() => parseVariableDefinition(r'$foo:'), throwsSyntaxError);
|
expect(() => parseVariableDefinition(r'$foo:'), throwsSyntaxError);
|
||||||
expect(() => parseVariableDefinition(r'$foo: int ='), throwsSyntaxError);
|
expect(() => parseVariableDefinition(r'$foo: int ='), throwsSyntaxError);
|
||||||
|
expect(() => parse(r'($foo: int = 2').parseVariableDefinitions(),
|
||||||
|
throwsSyntaxError);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue