Skip to content

Commit

Permalink
update HttpRequestKernel to use new parser; support variable expressi…
Browse files Browse the repository at this point in the history
…ons in request body (#3122)

* update kernel to use new parser; support expressions in req body

* FullText and FullSpan; exclude comments from Span

* pre-calculate IsSignificant and Span

* prevent kernel from sending requests when errors are present

* cleanup

* remove ParsedHttpRequest type

* make SyntaxTree.RootNode non-nullable

* fix nullability warning

* clean up tests to take kernel extension out of the loop

* improve variable sharing, add support for RequestValueInfos
  • Loading branch information
jonsequitur authored Aug 10, 2023
1 parent 8aa6534 commit a554838
Show file tree
Hide file tree
Showing 22 changed files with 464 additions and 463 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Microsoft.DotNet.Interactive.HttpRequest
public System.String Method { get;}
public System.String Uri { get;}
public System.String Version { get;}
public class HttpRequestKernel : Microsoft.DotNet.Interactive.Kernel, Microsoft.DotNet.Interactive.IKernelCommandHandler<Microsoft.DotNet.Interactive.Commands.RequestDiagnostics>, Microsoft.DotNet.Interactive.IKernelCommandHandler<Microsoft.DotNet.Interactive.Commands.RequestKernelInfo>, Microsoft.DotNet.Interactive.IKernelCommandHandler<Microsoft.DotNet.Interactive.Commands.RequestValue>, Microsoft.DotNet.Interactive.IKernelCommandHandler<Microsoft.DotNet.Interactive.Commands.SendValue>, Microsoft.DotNet.Interactive.IKernelCommandHandler<Microsoft.DotNet.Interactive.Commands.SubmitCode>, System.IDisposable
public class HttpRequestKernel : Microsoft.DotNet.Interactive.Kernel, Microsoft.DotNet.Interactive.IKernelCommandHandler<Microsoft.DotNet.Interactive.Commands.RequestDiagnostics>, Microsoft.DotNet.Interactive.IKernelCommandHandler<Microsoft.DotNet.Interactive.Commands.RequestKernelInfo>, Microsoft.DotNet.Interactive.IKernelCommandHandler<Microsoft.DotNet.Interactive.Commands.RequestValue>, Microsoft.DotNet.Interactive.IKernelCommandHandler<Microsoft.DotNet.Interactive.Commands.RequestValueInfos>, Microsoft.DotNet.Interactive.IKernelCommandHandler<Microsoft.DotNet.Interactive.Commands.SendValue>, Microsoft.DotNet.Interactive.IKernelCommandHandler<Microsoft.DotNet.Interactive.Commands.SubmitCode>, System.IDisposable
.ctor(System.String name = null, System.Net.Http.HttpClient client = null, System.Int32 responseDelayThresholdInMilliseconds = 1000, System.Int32 contentByteLengthThreshold = 500000)
public class HttpRequestKernelExtension
public static System.Void Load(Microsoft.DotNet.Interactive.Kernel kernel, System.Net.Http.HttpClient httpClient = null, System.Int32 responseDelayThresholdInMilliseconds = 1000, System.Int32 contentByteLengthThreshold = 500000)
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@ public partial class ParserTests
public class Comments
{
[Fact]
public void comments_are_parsed_correctly()
public void line_comment_before_method_and_url_is_parsed_correctly()
{
var result = Parse(
"""
var code = """
# This is a comment
GET https://example.com HTTP/1.1"
""");
GET https://example.com
""";

var result = Parse(code);

var methodNode = result.SyntaxTree.RootNode
.ChildNodes.Should().ContainSingle<HttpRequestNode>().Which
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public void header_with_body_is_parsed_correctly()
{
var result = Parse(
"""
POST https://example.com/comments HTTP/1.1
POST https://example.com/comments
Content-Type: application/xml
Authorization: token xxx

Expand All @@ -41,7 +41,7 @@ public void header_with_body_is_parsed_correctly()
}

[Fact]
public void header_separator_is_present()
public void header_separator_is_parsed()
{
var result = Parse(
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public void multiple_whitespaces_are_treated_as_a_single_token()

result.SyntaxTree.RootNode
.ChildNodes.Should().ContainSingle<HttpRequestNode>().Which
.MethodNode.ChildTokens.Single().TextWithTrivia.Should().Be(" \t ");
.MethodNode.ChildTokens.Single().Text.Should().Be(" \t ");
}

[Fact]
Expand All @@ -33,25 +33,25 @@ public void multiple_newlines_are_parsed_into_different_tokens()
var result = Parse("\n\v\r\n\n");

result.SyntaxTree.RootNode.ChildNodes.Should().ContainSingle<HttpRequestNode>().Which
.MethodNode.ChildTokens.Select(t => new { t.TextWithTrivia, t.Kind }).Should().BeEquivalentSequenceTo(
new { TextWithTrivia = "\n", Kind = HttpTokenKind.NewLine },
new { TextWithTrivia = "\v", Kind = HttpTokenKind.NewLine },
new { TextWithTrivia = "\r\n", Kind = HttpTokenKind.NewLine },
new { TextWithTrivia = "\n", Kind = HttpTokenKind.NewLine });
.MethodNode.ChildTokens.Select(t => new { t.Text, t.Kind }).Should().BeEquivalentSequenceTo(
new { Text = "\n", Kind = HttpTokenKind.NewLine },
new { Text = "\v", Kind = HttpTokenKind.NewLine },
new { Text = "\r\n", Kind = HttpTokenKind.NewLine },
new { Text = "\n", Kind = HttpTokenKind.NewLine });
}

[Fact]
public void multiple_punctuations_are_parsed_into_different_tokens()
{
var result = Parse(".!?.:/");
result.SyntaxTree.RootNode.ChildNodes.Should().ContainSingle<HttpRequestNode>().Which
.UrlNode.ChildTokens.Select(t => new { t.TextWithTrivia, t.Kind }).Should().BeEquivalentSequenceTo(
new { TextWithTrivia = ".", Kind = HttpTokenKind.Punctuation },
new { TextWithTrivia = "!", Kind = HttpTokenKind.Punctuation },
new { TextWithTrivia = "?", Kind = HttpTokenKind.Punctuation },
new { TextWithTrivia = ".", Kind = HttpTokenKind.Punctuation },
new { TextWithTrivia = ":", Kind = HttpTokenKind.Punctuation },
new { TextWithTrivia = "/", Kind = HttpTokenKind.Punctuation });
.UrlNode.ChildTokens.Select(t => new { t.Text, t.Kind }).Should().BeEquivalentSequenceTo(
new { Text = ".", Kind = HttpTokenKind.Punctuation },
new { Text = "!", Kind = HttpTokenKind.Punctuation },
new { Text = "?", Kind = HttpTokenKind.Punctuation },
new { Text = ".", Kind = HttpTokenKind.Punctuation },
new { Text = ":", Kind = HttpTokenKind.Punctuation },
new { Text = "/", Kind = HttpTokenKind.Punctuation });
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,35 @@ public void newline_is_legal_at_the_beginning_of_a_request()
GET https://example.com
""");

result.SyntaxTree.RootNode
.ChildNodes.Should().ContainSingle<HttpRequestNode>().Which
.MethodNode.ChildTokens.First().Kind.Should().Be(HttpTokenKind.NewLine);
var methodNode = result.SyntaxTree
.RootNode
.ChildNodes
.Should()
.ContainSingle<HttpRequestNode>().Which
.MethodNode;

methodNode.ChildTokens.First().Kind.Should().Be(HttpTokenKind.NewLine);
methodNode.Text.Should().Be("GET");
}

[Fact]
public void comment_is_legal_at_the_beginning_of_a_request()
{
var result = Parse(
"""
# this is a comment
GET https://example.com
""");

var methodNode = result.SyntaxTree
.RootNode
.ChildNodes
.Should()
.ContainSingle<HttpRequestNode>().Which
.MethodNode;

methodNode.ChildNodes.First().Should().BeOfType<HttpCommentNode>();
methodNode.Text.Should().Be("GET");
}

[Theory]
Expand All @@ -52,10 +78,10 @@ public void common_verbs_are_parsed_correctly(string line, string method)
}

[Theory]
[InlineData(@"GET https://example.com", "GET")]
[InlineData(@"Get https://example.com", "Get")]
[InlineData(@"OPTIONS https://example.com", "OPTIONS")]
[InlineData(@"options https://example.com", "options")]
[InlineData("GET https://example.com", "GET")]
[InlineData("Get https://example.com", "Get")]
[InlineData("OPTIONS https://example.com", "OPTIONS")]
[InlineData("options https://example.com", "options")]
public void it_can_parse_verbs_regardless_of_their_casing(string line, string method)
{
var result = Parse(line);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Linq;
using System.Net.Http;
using FluentAssertions;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@ public class Trivia
public void it_can_parse_an_empty_string()
{
var result = Parse("");
result.SyntaxTree.Should().BeNull();

// TODO: Test error reporting.
result.SyntaxTree.Should().NotBeNull();
}

[Fact]
Expand All @@ -28,9 +26,7 @@ public void it_can_parse_a_string_with_only_whitespace()

result.SyntaxTree.RootNode
.ChildNodes.Should().ContainSingle<HttpRequestNode>().Which
.MethodNode.ChildTokens.First().TextWithTrivia.Should().Be(" \t ");

// TODO: Test error reporting.
.MethodNode.ChildTokens.First().Text.Should().Be(" \t ");
}

[Fact]
Expand All @@ -40,7 +36,7 @@ public void it_can_parse_a_string_with_only_newlines()

result.SyntaxTree.RootNode
.ChildNodes.Should().ContainSingle<HttpRequestNode>().Which
.MethodNode.TextWithTrivia.Should().Be("\r\n\n\r\n");
.MethodNode.FullText.Should().Be("\r\n\n\r\n");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ private static HttpRequestParseResult Parse(string code)
var result = HttpRequestParser.Parse(code);
if (result.SyntaxTree?.RootNode is not null)
{
result.SyntaxTree.RootNode.TextWithTrivia.Should().Be(code);
result.SyntaxTree.RootNode.FullText.Should().Be(code);
}

return result;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,17 @@ public static AndWhichConstraint<ObjectAssertions, T> ContainSingle<T>(

return new AndWhichConstraint<ObjectAssertions, T>(subject.Should(), subject);
}

public static AndWhichConstraint<ObjectAssertions, T> ContainSingle<T>(
this GenericCollectionAssertions<HttpSyntaxNodeOrToken> should)
where T : HttpSyntaxNode
{
should.ContainSingle(e => e is T);

var subject = should.Subject
.OfType<T>()
.Single();

return new AndWhichConstraint<ObjectAssertions, T>(subject.Should(), subject);
}
}
Loading

0 comments on commit a554838

Please sign in to comment.