diff --git a/appveyor.yml b/appveyor.yml index b4bd3e1a..60e0f74d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: 5.0.1.{build} +version: 5.1.0.{build} pull_requests: do_not_increment_build_number: true skip_tags: true diff --git a/package.json b/package.json index e719dbcc..b639ca3a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "graphql-parser", - "version": "5.0.1", + "version": "5.1.0", "main": "index.js", "repository": "git@github.com:graphql-dotnet/parser.git", "author": "Joe McBride", diff --git a/src/Directory.Build.props b/src/Directory.Build.props index b434530f..714a5af9 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,10 +1,10 @@ - 5.0.1 + 5.1.0 Marek Magdziak $(NoWarn);1591 - Copyright 2016-2019 Marek Magdziak et al. All rights reserved. + Copyright 2016-2020 Marek Magdziak et al. All rights reserved. true latest diff --git a/src/GraphQLParser.Tests/ParserTests.cs b/src/GraphQLParser.Tests/ParserTests.cs index 472f4779..27f736cb 100644 --- a/src/GraphQLParser.Tests/ParserTests.cs +++ b/src/GraphQLParser.Tests/ParserTests.cs @@ -395,22 +395,39 @@ private static GraphQLDocument ParseGraphQLFieldWithOperationTypeAndNameSource() } [Theory] - [InlineData("directive @dir on FIELD_DEFINITION | ENUM_VALUE")] - [InlineData("directive @dir on | FIELD_DEFINITION | ENUM_VALUE")] + [InlineData("directive @dir repeatable on FIELD_DEFINITION", true)] + [InlineData("directive @dir(a: Int) repeatable on FIELD_DEFINITION", true)] + [InlineData("directive @dir on FIELD_DEFINITION | ENUM_VALUE", false)] + [InlineData("directive @dir on | FIELD_DEFINITION | ENUM_VALUE", false)] [InlineData(@"directive @dir on -FIELD_DEFINITION | ENUM_VALUE")] +FIELD_DEFINITION | ENUM_VALUE", false)] [InlineData(@"directive @dir on FIELD_DEFINITION -| ENUM_VALUE")] +| ENUM_VALUE", false)] [InlineData(@"directive @dir on | FIELD_DEFINITION -| ENUM_VALUE")] +| ENUM_VALUE", false)] [InlineData(@"directive @dir on | FIELD_DEFINITION -| ENUM_VALUE")] - public void Should_Parse_Directives(string text) +| ENUM_VALUE", false)] + public void Should_Parse_Directives(string text, bool repeatable) { - new Parser(new Lexer()).Parse(new Source(text)).ShouldNotBeNull(); + var document = new Parser(new Lexer()).Parse(new Source(text)); + document.ShouldNotBeNull(); + document.Definitions.Count.ShouldBe(1); + document.Definitions[0].ShouldBeOfType().Repeatable.ShouldBe(repeatable); + } + + [Theory] + [InlineData("directive @dir On FIELD_DEFINITION")] + [InlineData("directive @dir onn FIELD_DEFINITION")] + [InlineData("directive @dir Repeatable on FIELD_DEFINITION")] + [InlineData("directive @dir repeatablee on FIELD_DEFINITION")] + [InlineData("directive @dir repeatable On FIELD_DEFINITION")] + [InlineData("directive @dir repeatable onn FIELD_DEFINITION")] + public void Should_Throw_GraphQLSyntaxErrorException(string text) + { + Should.Throw(() => new Parser(new Lexer()).Parse(new Source(text))); } [Theory] diff --git a/src/GraphQLParser/AST/GraphQLDirectiveDefinition.cs b/src/GraphQLParser/AST/GraphQLDirectiveDefinition.cs index 3396c34d..e7d7150a 100644 --- a/src/GraphQLParser/AST/GraphQLDirectiveDefinition.cs +++ b/src/GraphQLParser/AST/GraphQLDirectiveDefinition.cs @@ -11,5 +11,7 @@ public class GraphQLDirectiveDefinition : GraphQLTypeDefinition public override ASTNodeKind Kind => ASTNodeKind.DirectiveDefinition; public List Locations { get; set; } + + public bool Repeatable { get; set; } } } \ No newline at end of file diff --git a/src/GraphQLParser/ParserContext.cs b/src/GraphQLParser/ParserContext.cs index 3b2c989d..4603b5ba 100644 --- a/src/GraphQLParser/ParserContext.cs +++ b/src/GraphQLParser/ParserContext.cs @@ -347,8 +347,14 @@ private GraphQLDirective ParseDirective() Arguments = ParseArguments(), Location = GetLocation(start) }; - } - + } + + /// + /// http://spec.graphql.org/draft/#DirectiveDefinition + /// DirectiveDefinition: + /// Description(opt) directive @ Name ArgumentsDefinition(opt) repeatable(opt) on DirectiveLocations + /// + /// private GraphQLDirectiveDefinition ParseDirectiveDefinition() { var comment = GetComment(); @@ -358,6 +364,7 @@ private GraphQLDirectiveDefinition ParseDirectiveDefinition() var name = ParseName(); var args = ParseArgumentDefs(); + var repeatable = ParseRepeatable(); ExpectKeyword("on"); var locations = ParseDirectiveLocations(); @@ -366,10 +373,30 @@ private GraphQLDirectiveDefinition ParseDirectiveDefinition() { Comment = comment, Name = name, + Repeatable = repeatable, Arguments = args, Locations = locations, Location = GetLocation(start) }; + } + + private bool ParseRepeatable() + { + if (Peek(TokenKind.NAME)) + { + switch (currentToken.Value) + { + case "repeatable": + Advance(); + return true; + case "on": + return false; + default: + throw new GraphQLSyntaxErrorException($"Unexpected {currentToken}", source, currentToken.Start); + } + } + + return false; } private List ParseDirectiveLocations()