diff --git a/.idea/runConfigurations/tests_in_value_test_dart.xml b/.idea/runConfigurations/tests_in_value_test_dart.xml
new file mode 100644
index 00000000..58aca4ef
--- /dev/null
+++ b/.idea/runConfigurations/tests_in_value_test_dart.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/graphql_parser/CHANGELOG.md b/graphql_parser/CHANGELOG.md
new file mode 100644
index 00000000..5602c605
--- /dev/null
+++ b/graphql_parser/CHANGELOG.md
@@ -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.
\ No newline at end of file
diff --git a/graphql_parser/README.md b/graphql_parser/README.md
index 9d7ea6bc..56a23f79 100644
--- a/graphql_parser/README.md
+++ b/graphql_parser/README.md
@@ -1,8 +1,8 @@
# 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,
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:
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
import 'package:graphql_parser/graphql_parser.dart';
@@ -31,6 +34,10 @@ doSomething(String text) {
var tokens = scan(text);
var parser = new Parser(tokens);
+ if (parser.errors.isNotEmpty) {
+ // Handle errors...
+ }
+
// Parse the GraphQL document using recursive descent
var doc = parser.parseDocument();
diff --git a/graphql_parser/example/example.dart b/graphql_parser/example/example.dart
index 4a842895..34566298 100644
--- a/graphql_parser/example/example.dart
+++ b/graphql_parser/example/example.dart
@@ -1,6 +1,6 @@
import 'package:graphql_parser/graphql_parser.dart';
-final String INPUT = '''
+final String text = '''
{
project(name: "GraphQL") {
tagline
@@ -10,7 +10,7 @@ final String INPUT = '''
.trim();
main() {
- var tokens = scan(INPUT);
+ var tokens = scan(text);
var parser = new Parser(tokens);
var doc = parser.parseDocument();
diff --git a/graphql_parser/example/visitor.dart b/graphql_parser/example/visitor.dart
deleted file mode 100644
index c9301037..00000000
--- a/graphql_parser/example/visitor.dart
+++ /dev/null
@@ -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 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 data;
- final Map result = {};
-
- MapQuerier(this.data);
-
- Map 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];
- }
-}
diff --git a/graphql_parser/lib/visitor.dart b/graphql_parser/lib/visitor.dart
deleted file mode 100644
index 8fa22936..00000000
--- a/graphql_parser/lib/visitor.dart
+++ /dev/null
@@ -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) {}
-}
diff --git a/graphql_parser/pubspec.yaml b/graphql_parser/pubspec.yaml
index 0bf80c70..40cda92f 100644
--- a/graphql_parser/pubspec.yaml
+++ b/graphql_parser/pubspec.yaml
@@ -1,5 +1,5 @@
name: graphql_parser
-version: 1.0.0+1
+version: 1.1.0
description: Parses GraphQL queries and schemas.
author: Tobe O
homepage: https://github.com/thosakwe/graphql_parser
diff --git a/graphql_parser/test/argument_test.dart b/graphql_parser/test/argument_test.dart
index f6d53a82..ba473144 100644
--- a/graphql_parser/test/argument_test.dart
+++ b/graphql_parser/test/argument_test.dart
@@ -1,6 +1,7 @@
import 'package:graphql_parser/graphql_parser.dart';
import 'package:matcher/matcher.dart';
import 'package:test/test.dart';
+
import 'common.dart';
main() {
@@ -10,13 +11,24 @@ main() {
});
test('exception', () {
- expect(() => parseArgument('foo'), throwsSyntaxError);
- expect(() => parseArgument('foo:'), throwsSyntaxError);
- expect(() => parseArgumentList(r'(foo: $bar'), throwsSyntaxError);
+ var isSyntaxError = predicate((x) {
+ var parser = parse(x.toString())..parseArgument();
+ 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();
+
List parseArgumentList(String text) =>
parse(text).parseArguments();
diff --git a/graphql_parser/test/common.dart b/graphql_parser/test/common.dart
index 5551d83f..50194027 100644
--- a/graphql_parser/test/common.dart
+++ b/graphql_parser/test/common.dart
@@ -2,7 +2,4 @@ import 'package:graphql_parser/graphql_parser.dart';
import 'package:matcher/matcher.dart';
import 'package:test/test.dart';
-Parser parse(String text) => new Parser(scan(text));
-
-final Matcher throwsSyntaxError =
- throwsA(predicate((x) => x is SyntaxError, 'is a syntax error'));
+Parser parse(String text) => new Parser(scan(text));
\ No newline at end of file
diff --git a/graphql_parser/test/directive_test.dart b/graphql_parser/test/directive_test.dart
index 511ca4f9..c1412a0f 100644
--- a/graphql_parser/test/directive_test.dart
+++ b/graphql_parser/test/directive_test.dart
@@ -21,11 +21,16 @@ main() {
});
test('exceptions', () {
- expect(() => parseDirective('@'), throwsSyntaxError);
- expect(() => parseDirective('@foo:'), throwsSyntaxError);
- expect(() => parseDirective('@foo ('), throwsSyntaxError);
- expect(() => parseDirective('@foo (bar: 2'), throwsSyntaxError);
- expect(() => parseDirective('@foo ()'), throwsSyntaxError);
+ var isSyntaxError = predicate((x) {
+ var parser = parse(x.toString())..parseDirective();
+ return parser.errors.isNotEmpty;
+ }, 'fails to parse directive');
+
+ expect('@', isSyntaxError);
+ expect('@foo:', isSyntaxError);
+ expect('@foo (', isSyntaxError);
+ expect('@foo (bar: 2', isSyntaxError);
+ expect('@foo (', isSyntaxError);
});
}
diff --git a/graphql_parser/test/document_test.dart b/graphql_parser/test/document_test.dart
index 2b882232..914f6b43 100644
--- a/graphql_parser/test/document_test.dart
+++ b/graphql_parser/test/document_test.dart
@@ -1,5 +1,6 @@
import 'package:graphql_parser/graphql_parser.dart';
import 'package:test/test.dart';
+
import 'common.dart';
import 'directive_test.dart';
import 'field_test.dart';
@@ -34,14 +35,15 @@ main() {
});
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);
+ var throwsSyntaxError = predicate((x) {
+ var parser = parse(x.toString())..parseFragmentDefinition();
+ return parser.errors.isNotEmpty;
+ }, 'fails to parse fragment definition');
+
+ expect('fragment', throwsSyntaxError);
+ expect('fragment foo', throwsSyntaxError);
+ expect('fragment foo on', throwsSyntaxError);
+ expect('fragment foo on bar', throwsSyntaxError);
});
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', () {
var doc =
parse(r'query foo ($one: [int] = 2) @foo @bar: 2 {foo, bar: baz}')
@@ -91,10 +107,13 @@ main() {
});
test('exceptions', () {
- expect(
- () => parse('query').parseOperationDefinition(), throwsSyntaxError);
- expect(() => parse('query foo()').parseOperationDefinition(),
- throwsSyntaxError);
+ var throwsSyntaxError = predicate((x) {
+ var parser = parse(x.toString())..parseOperationDefinition();
+ return parser.errors.isNotEmpty;
+ }, 'fails to parse operation definition');
+
+ expect('query', throwsSyntaxError);
+ expect('query foo()', throwsSyntaxError);
});
});
}
diff --git a/graphql_parser/test/field_test.dart b/graphql_parser/test/field_test.dart
index 3efd91a6..a0b3634e 100644
--- a/graphql_parser/test/field_test.dart
+++ b/graphql_parser/test/field_test.dart
@@ -1,7 +1,8 @@
import 'package:graphql_parser/graphql_parser.dart';
import 'package:test/test.dart';
-import 'common.dart';
+
import 'argument_test.dart';
+import 'common.dart';
import 'directive_test.dart';
import 'fragment_spread_test.dart';
import 'selection_set_test.dart';
@@ -16,7 +17,12 @@ main() {
expect('foo: bar', isFieldName('foo', alias: 'bar'));
});
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);
});
});
diff --git a/graphql_parser/test/inline_fragment_test.dart b/graphql_parser/test/inline_fragment_test.dart
index 2ec374fb..5ad6fbca 100644
--- a/graphql_parser/test/inline_fragment_test.dart
+++ b/graphql_parser/test/inline_fragment_test.dart
@@ -31,10 +31,14 @@ main() {
});
test('exceptions', () {
- expect(() => parseInlineFragment('... on foo'), throwsSyntaxError);
- expect(() => parseInlineFragment('... on foo @bar'), throwsSyntaxError);
- expect(() => parseInlineFragment('... on'), throwsSyntaxError);
- expect(() => parseInlineFragment('...'), throwsSyntaxError);
+ var throwsSyntaxError = predicate((x) {
+ var parser = parse(x.toString())..parseInlineFragment();
+ return parser.errors.isNotEmpty;
+ }, 'fails to parse inline fragment');
+ expect('... on foo', throwsSyntaxError);
+ expect('... on foo @bar', throwsSyntaxError);
+ expect('... on', throwsSyntaxError);
+ expect('...', throwsSyntaxError);
});
}
diff --git a/graphql_parser/test/selection_set_test.dart b/graphql_parser/test/selection_set_test.dart
index 39497226..fd7a094e 100644
--- a/graphql_parser/test/selection_set_test.dart
+++ b/graphql_parser/test/selection_set_test.dart
@@ -43,7 +43,12 @@ main() {
});
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);
});
}
diff --git a/graphql_parser/test/type_test.dart b/graphql_parser/test/type_test.dart
index f723b3c7..7fa12109 100644
--- a/graphql_parser/test/type_test.dart
+++ b/graphql_parser/test/type_test.dart
@@ -35,8 +35,13 @@ main() {
});
test('exceptions', () {
- expect(() => parseType('[foo'), throwsSyntaxError);
- expect(() => parseType('['), throwsSyntaxError);
+ var throwsSyntaxError = predicate((x) {
+ var parser = parse(x.toString())..parseType();
+ return parser.errors.isNotEmpty;
+ }, 'fails to parse type');
+
+ expect('[foo', throwsSyntaxError);
+ expect('[', throwsSyntaxError);
});
});
}
diff --git a/graphql_parser/test/value_test.dart b/graphql_parser/test/value_test.dart
index 640c5430..61e90f48 100644
--- a/graphql_parser/test/value_test.dart
+++ b/graphql_parser/test/value_test.dart
@@ -1,6 +1,8 @@
import 'dart:math' as math;
+
import 'package:graphql_parser/graphql_parser.dart';
import 'package:test/test.dart';
+
import 'common.dart';
main() {
@@ -40,12 +42,38 @@ main() {
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({}));
+ expect('{a: 2}', isValue({'a': 2}));
+ expect('{a: 2, b: "c"}', isValue({'a': 2, 'b': 'c'}));
+ });
+
+ test('null', () {
+ expect('null', isValue(null));
+ });
+
+ test('enum', () {
+ expect('FOO_BAR', isValue('FOO_BAR'));
+ });
+
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();
+
Matcher isValue(value) => new _IsValue(value);
class _IsValue extends Matcher {
diff --git a/graphql_parser/test/variable_definition_test.dart b/graphql_parser/test/variable_definition_test.dart
index 0799bf35..04f85987 100644
--- a/graphql_parser/test/variable_definition_test.dart
+++ b/graphql_parser/test/variable_definition_test.dart
@@ -1,5 +1,6 @@
import 'package:graphql_parser/graphql_parser.dart';
import 'package:test/test.dart';
+
import 'common.dart';
import 'type_test.dart';
import 'value_test.dart';
@@ -18,11 +19,21 @@ main() {
});
test('exceptions', () {
- expect(() => parseVariableDefinition(r'$foo'), throwsSyntaxError);
- expect(() => parseVariableDefinition(r'$foo:'), throwsSyntaxError);
- expect(() => parseVariableDefinition(r'$foo: int ='), throwsSyntaxError);
- expect(() => parse(r'($foo: int = 2').parseVariableDefinitions(),
- throwsSyntaxError);
+ var throwsSyntaxError = predicate((x) {
+ var parser = parse(x.toString())..parseVariableDefinition();
+ return parser.errors.isNotEmpty;
+ }, 'fails to parse variable definition');
+
+ 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);
});
}
diff --git a/graphql_parser/test/variable_test.dart b/graphql_parser/test/variable_test.dart
index ac7aa566..d4484666 100644
--- a/graphql_parser/test/variable_test.dart
+++ b/graphql_parser/test/variable_test.dart
@@ -1,4 +1,5 @@
import 'package:test/test.dart';
+
import 'common.dart';
main() {
@@ -12,7 +13,12 @@ main() {
});
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);
});
}