Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce object model for http requests and responses #2950

Merged
merged 3 commits into from
Apr 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,12 +1,30 @@
Microsoft.DotNet.Interactive.HttpRequest
public class HttpContent
.ctor(System.String raw, System.Int64 byteLength, System.Collections.Generic.Dictionary<System.String,System.String[]> headers, System.String contentType = null)
public System.Int64 ByteLength { get;}
public System.String ContentType { get;}
public System.Collections.Generic.Dictionary<System.String,System.String[]> Headers { get;}
public System.String Raw { get;}
public class HttpRequest
.ctor(System.String method, System.String version, System.Collections.Generic.Dictionary<System.String,System.String[]> headers, System.String uri = null, HttpContent content = null)
public HttpContent Content { get;}
public System.Collections.Generic.Dictionary<System.String,System.String[]> Headers { get;}
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
.ctor(System.String name = null, System.Net.Http.HttpClient client = null)
public System.Uri BaseAddress { get; set;}
public System.Threading.Tasks.Task HandleAsync(Microsoft.DotNet.Interactive.Commands.RequestValue command, Microsoft.DotNet.Interactive.KernelInvocationContext context)
public System.Threading.Tasks.Task HandleAsync(Microsoft.DotNet.Interactive.Commands.SendValue command, Microsoft.DotNet.Interactive.KernelInvocationContext context)
public System.Threading.Tasks.Task HandleAsync(Microsoft.DotNet.Interactive.Commands.SubmitCode command, Microsoft.DotNet.Interactive.KernelInvocationContext context)
public System.Threading.Tasks.Task HandleAsync(Microsoft.DotNet.Interactive.Commands.RequestDiagnostics command, Microsoft.DotNet.Interactive.KernelInvocationContext context)
public System.Void SetValue(System.String valueName, System.String value)
public class HttpRequestKernelExtension
public static System.Void Load(Microsoft.DotNet.Interactive.Kernel kernel)
public static System.Void Load(Microsoft.DotNet.Interactive.Kernel kernel, System.Net.Http.HttpClient httpClient = null)
.ctor()
public class HttpResponse
.ctor(System.Int32 statusCode, System.String reasonPhrase, System.String version, System.Collections.Generic.Dictionary<System.String,System.String[]> headers, HttpRequest request = null, HttpContent content = null, System.Nullable<System.Double> elapsedMilliseconds = null)
public HttpContent Content { get;}
public System.Nullable<System.Double> ElapsedMilliseconds { get;}
public System.Collections.Generic.Dictionary<System.String,System.String[]> Headers { get;}
public System.String ReasonPhrase { get;}
public HttpRequest Request { get;}
public System.Int32 StatusCode { get;}
public System.String Version { get;}
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,19 @@
using FluentAssertions;
using Microsoft.DotNet.Interactive.Commands;
using Microsoft.DotNet.Interactive.Events;
using Microsoft.DotNet.Interactive.Formatting;
using Microsoft.DotNet.Interactive.Tests.Utility;
using Xunit;

namespace Microsoft.DotNet.Interactive.HttpRequest.Tests;

public class HttpRequestKernelTests
{
public HttpRequestKernelTests()
{
Formatter.ResetToDefault();
}

[Theory]
[InlineData("GET")]
[InlineData("PUT")]
Expand All @@ -41,7 +47,6 @@ public async Task supports_verbs(string verb)
result.Events.Should().NotContainErrors();

request.Method.Method.Should().Be(verb);

}

[Fact]
Expand All @@ -51,7 +56,7 @@ public async Task requires_base_address_when_using_relative_uris()

var result = await kernel.SendAsync(new SubmitCode("get /relativePath"));

var error = result.Events.Should().ContainSingle<CommandFailed>().Which;
var error = result.Events.Should().ContainSingle<CommandFailed>().Which;

error.Message.Should().Contain("Cannot use relative path /relativePath without a base address.");
}
Expand All @@ -69,7 +74,7 @@ public async Task ignores_base_address_when_using_absolute_paths()
var client = new HttpClient(handler);
using var kernel = new HttpRequestKernel(client: client);
kernel.BaseAddress = new Uri("http://example.com");

var result = await kernel.SendAsync(new SubmitCode("get https://anotherlocation.com/endpoint"));

result.Events.Should().NotContainErrors();
Expand All @@ -89,7 +94,7 @@ public async Task can_replace_symbols()
});
var client = new HttpClient(handler);
using var kernel = new HttpRequestKernel(client: client);

kernel.SetValue("my_host", "my.host.com");

var result = await kernel.SendAsync(new SubmitCode("get https://{{my_host}}:1200/endpoint"));
Expand Down Expand Up @@ -123,7 +128,7 @@ public async Task can_use_base_address_to_resolve_host_symbol()
[Fact]
public async Task can_handle_multiple_request_in_a_single_submission()
{
List<HttpRequestMessage> requests = new ();
List<HttpRequestMessage> requests = new();
var handler = new InterceptingHttpMessageHandler((message, _) =>
{
requests.Add(message);
Expand All @@ -140,7 +145,7 @@ public async Task can_handle_multiple_request_in_a_single_submission()

result.Events.Should().NotContainErrors();

requests.Select(r => r.RequestUri.AbsoluteUri).ToArray().Should().BeEquivalentTo(new []{ "https://location1.com:1200/endpoint", "https://location2.com:1200/endpoint" });
requests.Select(r => r.RequestUri.AbsoluteUri).ToArray().Should().BeEquivalentTo(new[] { "https://location1.com:1200/endpoint", "https://location2.com:1200/endpoint" });
}

[Fact]
Expand Down Expand Up @@ -227,7 +232,7 @@ public async Task can_use_symbols_in_body()
});
var client = new HttpClient(handler);
using var kernel = new HttpRequestKernel(client: client);
kernel.SetValue("one","1");
kernel.SetValue("one", "1");
var result = await kernel.SendAsync(new SubmitCode(@"
post https://location1.com:1200/endpoint
Authorization: Basic username password
Expand Down Expand Up @@ -352,4 +357,32 @@ public async Task multiple_diagnostics_are_returned_from_the_same_submission()

diagnostics.Diagnostics.Should().HaveCount(2);
}

[Fact]
public async Task produces_json_html_and_plain_text_formatted_values()
{
HttpRequestMessage request = null;
var handler = new InterceptingHttpMessageHandler((message, _) =>
{
request = message;
var response = new HttpResponseMessage(HttpStatusCode.OK);
response.RequestMessage = message;
return Task.FromResult(response);
});
var client = new HttpClient(handler);

using var root = new CompositeKernel();
HttpRequestKernelExtension.Load(root, client);
var kernel = root.FindKernels(k => k is HttpRequestKernel).Single();

var result = await kernel.SendAsync(new SubmitCode($"GET http://testuri.ninja"));

result.Events.Should().NotContainErrors();

var displayEvent =
result.Events.Should().ContainSingle<DisplayEvent>().Which.FormattedValues.Should()
.Contain(f => f.MimeType == HtmlFormatter.MimeType).And
.Contain(f => f.MimeType == PlainTextFormatter.MimeType).And
.Contain(f => f.MimeType == JsonFormatter.MimeType);
}
}
Loading