Bump graphql_parser -> 1.1.0

This commit is contained in:
Tobe O 2018-08-04 20:44:41 -04:00
parent 5b0062b657
commit 84e204267b
18 changed files with 166 additions and 220 deletions

View file

@ -0,0 +1,7 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="tests in value_test.dart" type="DartTestRunConfigurationType" factoryName="Dart Test" singleton="true" nameIsGenerated="true">
<option name="filePath" value="$PROJECT_DIR$/graphql_parser/test/value_test.dart" />
<option name="testRunnerOptions" value="-j4" />
<method />
</configuration>
</component>

View file

@ -0,0 +1,9 @@
# 1.1.0
* Removed `GraphQLVisitor`.
* Enable parsing operations without an explicit
name.
* Parse `null`.
* Completely ignore commas.
* Ignore Unicode BOM, as per the spec.
* Parse object values.
* Parse enum values.

View file

@ -1,8 +1,8 @@
# graphql_parser # graphql_parser
[![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/angel-dart/graphql.svg)](https://travis-ci.org/angel-dart/graphql)
Parses GraphQL queries and schemas. Also includes a `GraphQLVisitor` class. Parses GraphQL queries and schemas.
*This library is merely a parser/visitor*. 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.
@ -24,6 +24,9 @@ dependencies:
The AST featured in this library is directly based off this ANTLR4 grammar created by Joseph T. McBride: The AST featured in this library is directly based off this ANTLR4 grammar created by Joseph T. McBride:
https://github.com/antlr/grammars-v4/blob/master/graphql/GraphQL.g4 https://github.com/antlr/grammars-v4/blob/master/graphql/GraphQL.g4
It has since been updated to reflect upon the grammar in the official GraphQL
specification.
```dart ```dart
import 'package:graphql_parser/graphql_parser.dart'; import 'package:graphql_parser/graphql_parser.dart';
@ -31,6 +34,10 @@ doSomething(String text) {
var tokens = scan(text); var tokens = scan(text);
var parser = new Parser(tokens); var parser = new Parser(tokens);
if (parser.errors.isNotEmpty) {
// Handle errors...
}
// Parse the GraphQL document using recursive descent // Parse the GraphQL document using recursive descent
var doc = parser.parseDocument(); var doc = parser.parseDocument();

View file

@ -1,6 +1,6 @@
import 'package:graphql_parser/graphql_parser.dart'; import 'package:graphql_parser/graphql_parser.dart';
final String INPUT = ''' final String text = '''
{ {
project(name: "GraphQL") { project(name: "GraphQL") {
tagline tagline
@ -10,7 +10,7 @@ final String INPUT = '''
.trim(); .trim();
main() { main() {
var tokens = scan(INPUT); var tokens = scan(text);
var parser = new Parser(tokens); var parser = new Parser(tokens);
var doc = parser.parseDocument(); var doc = parser.parseDocument();

View file

@ -1,50 +0,0 @@
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];
}
}

View file

@ -1,125 +0,0 @@
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 ListValueContext) visitArrayValue(ctx);
}
visitStringValue(StringValueContext ctx) {}
visitBooleanValue(BooleanValueContext ctx) {}
visitNumberValue(NumberValueContext ctx) {}
visitArrayValue(ListValueContext 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) {}
}

View file

@ -1,5 +1,5 @@
name: graphql_parser name: graphql_parser
version: 1.0.0+1 version: 1.1.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

View file

@ -1,6 +1,7 @@
import 'package:graphql_parser/graphql_parser.dart'; import 'package:graphql_parser/graphql_parser.dart';
import 'package:matcher/matcher.dart'; import 'package:matcher/matcher.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import 'common.dart'; import 'common.dart';
main() { main() {
@ -10,13 +11,24 @@ main() {
}); });
test('exception', () { test('exception', () {
expect(() => parseArgument('foo'), throwsSyntaxError); var isSyntaxError = predicate((x) {
expect(() => parseArgument('foo:'), throwsSyntaxError); var parser = parse(x.toString())..parseArgument();
expect(() => parseArgumentList(r'(foo: $bar'), throwsSyntaxError); return parser.errors.isNotEmpty;
}, 'fails to parse argument');
var isSyntaxErrorOnArguments = predicate((x) {
var parser = parse(x.toString())..parseArguments();
return parser.errors.isNotEmpty;
}, 'fails to parse arguments');
expect('foo', isSyntaxError);
expect('foo:', isSyntaxError);
expect(r'(foo: $bar', isSyntaxErrorOnArguments);
}); });
} }
ArgumentContext parseArgument(String text) => parse(text).parseArgument(); ArgumentContext parseArgument(String text) => parse(text).parseArgument();
List<ArgumentContext> parseArgumentList(String text) => List<ArgumentContext> parseArgumentList(String text) =>
parse(text).parseArguments(); parse(text).parseArguments();

View file

@ -3,6 +3,3 @@ import 'package:matcher/matcher.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
Parser parse(String text) => new Parser(scan(text)); Parser parse(String text) => new Parser(scan(text));
final Matcher throwsSyntaxError =
throwsA(predicate((x) => x is SyntaxError, 'is a syntax error'));

View file

@ -21,11 +21,16 @@ main() {
}); });
test('exceptions', () { test('exceptions', () {
expect(() => parseDirective('@'), throwsSyntaxError); var isSyntaxError = predicate((x) {
expect(() => parseDirective('@foo:'), throwsSyntaxError); var parser = parse(x.toString())..parseDirective();
expect(() => parseDirective('@foo ('), throwsSyntaxError); return parser.errors.isNotEmpty;
expect(() => parseDirective('@foo (bar: 2'), throwsSyntaxError); }, 'fails to parse directive');
expect(() => parseDirective('@foo ()'), throwsSyntaxError);
expect('@', isSyntaxError);
expect('@foo:', isSyntaxError);
expect('@foo (', isSyntaxError);
expect('@foo (bar: 2', isSyntaxError);
expect('@foo (', isSyntaxError);
}); });
} }

View file

@ -1,5 +1,6 @@
import 'package:graphql_parser/graphql_parser.dart'; import 'package:graphql_parser/graphql_parser.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import 'common.dart'; import 'common.dart';
import 'directive_test.dart'; import 'directive_test.dart';
import 'field_test.dart'; import 'field_test.dart';
@ -34,14 +35,15 @@ main() {
}); });
test('fragment exceptions', () { test('fragment exceptions', () {
expect( var throwsSyntaxError = predicate((x) {
() => parse('fragment').parseFragmentDefinition(), throwsSyntaxError); var parser = parse(x.toString())..parseFragmentDefinition();
expect(() => parse('fragment foo').parseFragmentDefinition(), return parser.errors.isNotEmpty;
throwsSyntaxError); }, 'fails to parse fragment definition');
expect(() => parse('fragment foo on').parseFragmentDefinition(),
throwsSyntaxError); expect('fragment', throwsSyntaxError);
expect(() => parse('fragment foo on bar').parseFragmentDefinition(), expect('fragment foo', throwsSyntaxError);
throwsSyntaxError); expect('fragment foo on', throwsSyntaxError);
expect('fragment foo on bar', throwsSyntaxError);
}); });
group('operation', () { group('operation', () {
@ -59,6 +61,20 @@ main() {
])); ]));
}); });
test('mutation', () {
var op = parse('mutation {foo, bar: baz}').parseOperationDefinition();
expect(op.variableDefinitions, isNull);
expect(op.isQuery, isFalse);
expect(op.isMutation, isTrue);
expect(op.name, isNull);
expect(
op.selectionSet,
isSelectionSet([
isField(fieldName: isFieldName('foo')),
isField(fieldName: isFieldName('bar', alias: 'baz'))
]));
});
test('with operation type', () { test('with operation type', () {
var doc = var doc =
parse(r'query foo ($one: [int] = 2) @foo @bar: 2 {foo, bar: baz}') parse(r'query foo ($one: [int] = 2) @foo @bar: 2 {foo, bar: baz}')
@ -91,10 +107,13 @@ main() {
}); });
test('exceptions', () { test('exceptions', () {
expect( var throwsSyntaxError = predicate((x) {
() => parse('query').parseOperationDefinition(), throwsSyntaxError); var parser = parse(x.toString())..parseOperationDefinition();
expect(() => parse('query foo()').parseOperationDefinition(), return parser.errors.isNotEmpty;
throwsSyntaxError); }, 'fails to parse operation definition');
expect('query', throwsSyntaxError);
expect('query foo()', throwsSyntaxError);
}); });
}); });
} }

View file

@ -1,7 +1,8 @@
import 'package:graphql_parser/graphql_parser.dart'; import 'package:graphql_parser/graphql_parser.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import 'common.dart';
import 'argument_test.dart'; import 'argument_test.dart';
import 'common.dart';
import 'directive_test.dart'; import 'directive_test.dart';
import 'fragment_spread_test.dart'; import 'fragment_spread_test.dart';
import 'selection_set_test.dart'; import 'selection_set_test.dart';
@ -16,7 +17,12 @@ main() {
expect('foo: bar', isFieldName('foo', alias: 'bar')); expect('foo: bar', isFieldName('foo', alias: 'bar'));
}); });
test('exceptions', () { test('exceptions', () {
expect(() => parseFieldName('foo:'), throwsSyntaxError); var throwsSyntaxError = predicate((x) {
var parser = parse(x.toString())..parseFieldName();
return parser.errors.isNotEmpty;
}, 'fails to parse field name');
expect('foo:', throwsSyntaxError);
}); });
}); });

View file

@ -31,10 +31,14 @@ main() {
}); });
test('exceptions', () { test('exceptions', () {
expect(() => parseInlineFragment('... on foo'), throwsSyntaxError); var throwsSyntaxError = predicate((x) {
expect(() => parseInlineFragment('... on foo @bar'), throwsSyntaxError); var parser = parse(x.toString())..parseInlineFragment();
expect(() => parseInlineFragment('... on'), throwsSyntaxError); return parser.errors.isNotEmpty;
expect(() => parseInlineFragment('...'), throwsSyntaxError); }, 'fails to parse inline fragment');
expect('... on foo', throwsSyntaxError);
expect('... on foo @bar', throwsSyntaxError);
expect('... on', throwsSyntaxError);
expect('...', throwsSyntaxError);
}); });
} }

View file

@ -43,7 +43,12 @@ main() {
}); });
test('exceptions', () { test('exceptions', () {
expect(() => parseSelectionSet('{foo,bar,baz'), throwsSyntaxError); var throwsSyntaxError = predicate((x) {
var parser = parse(x.toString())..parseSelectionSet();
return parser.errors.isNotEmpty;
}, 'fails to parse selection set');
expect('{foo,bar,baz', throwsSyntaxError);
}); });
} }

View file

@ -35,8 +35,13 @@ main() {
}); });
test('exceptions', () { test('exceptions', () {
expect(() => parseType('[foo'), throwsSyntaxError); var throwsSyntaxError = predicate((x) {
expect(() => parseType('['), throwsSyntaxError); var parser = parse(x.toString())..parseType();
return parser.errors.isNotEmpty;
}, 'fails to parse type');
expect('[foo', throwsSyntaxError);
expect('[', throwsSyntaxError);
}); });
}); });
} }

View file

@ -1,6 +1,8 @@
import 'dart:math' as math; import 'dart:math' as math;
import 'package:graphql_parser/graphql_parser.dart'; import 'package:graphql_parser/graphql_parser.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import 'common.dart'; import 'common.dart';
main() { main() {
@ -40,12 +42,38 @@ main() {
expect('"\\u0123\\u4567"', isValue('\u0123\u4567')); expect('"\\u0123\\u4567"', isValue('\u0123\u4567'));
}); });
test('block string', () {
expect('""""""', isValue(''));
expect('"""abc"""', isValue('abc'));
expect('"""\n\n\nabc\n\n\n"""', isValue('abc'));
});
test('object', () {
expect('{}', isValue(<String, dynamic>{}));
expect('{a: 2}', isValue(<String, dynamic>{'a': 2}));
expect('{a: 2, b: "c"}', isValue(<String, dynamic>{'a': 2, 'b': 'c'}));
});
test('null', () {
expect('null', isValue(null));
});
test('enum', () {
expect('FOO_BAR', isValue('FOO_BAR'));
});
test('exceptions', () { test('exceptions', () {
expect(() => parseValue('[1'), throwsSyntaxError); var throwsSyntaxError = predicate((x) {
var parser = parse(x.toString())..parseValue();
return parser.errors.isNotEmpty;
}, 'fails to parse value');
expect('[1', throwsSyntaxError);
}); });
} }
ValueContext parseValue(String text) => parse(text).parseValue(); ValueContext parseValue(String text) => parse(text).parseValue();
Matcher isValue(value) => new _IsValue(value); Matcher isValue(value) => new _IsValue(value);
class _IsValue extends Matcher { class _IsValue extends Matcher {

View file

@ -1,5 +1,6 @@
import 'package:graphql_parser/graphql_parser.dart'; import 'package:graphql_parser/graphql_parser.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import 'common.dart'; import 'common.dart';
import 'type_test.dart'; import 'type_test.dart';
import 'value_test.dart'; import 'value_test.dart';
@ -18,11 +19,21 @@ main() {
}); });
test('exceptions', () { test('exceptions', () {
expect(() => parseVariableDefinition(r'$foo'), throwsSyntaxError); var throwsSyntaxError = predicate((x) {
expect(() => parseVariableDefinition(r'$foo:'), throwsSyntaxError); var parser = parse(x.toString())..parseVariableDefinition();
expect(() => parseVariableDefinition(r'$foo: int ='), throwsSyntaxError); return parser.errors.isNotEmpty;
expect(() => parse(r'($foo: int = 2').parseVariableDefinitions(), }, 'fails to parse variable definition');
throwsSyntaxError);
var throwsSyntaxErrorOnDefinitions = predicate((x) {
var parser = parse(x.toString())..parseVariableDefinitions();
return parser.errors.isNotEmpty;
}, 'fails to parse variable definitions');
expect(r'$foo', throwsSyntaxError);
expect(r'$foo:', throwsSyntaxError);
expect(r'$foo: int =', throwsSyntaxError);
expect(r'($foo: int = 2', throwsSyntaxErrorOnDefinitions);
}); });
} }

View file

@ -1,4 +1,5 @@
import 'package:test/test.dart'; import 'package:test/test.dart';
import 'common.dart'; import 'common.dart';
main() { main() {
@ -12,7 +13,12 @@ main() {
}); });
test('exceptions', () { test('exceptions', () {
expect(() => parse(r'$').parseVariable(), throwsSyntaxError); var throwsSyntaxError = predicate((x) {
var parser = parse(x.toString())..parseVariable();
return parser.errors.isNotEmpty;
}, 'fails to parse variable');
expect(r'$', throwsSyntaxError);
}); });
} }