#699 no dynamic support in STJ, none in Flurl now either

This commit is contained in:
Todd 2022-06-03 15:17:23 -05:00
parent a53f46afff
commit ac6b55e432
12 changed files with 60 additions and 299 deletions

View File

@ -43,7 +43,7 @@ namespace Flurl.Test.Http
HttpTest.RespondWith("{ \"invalid JSON!");
try {
await "http://myapi.com".GetJsonAsync();
await "http://myapi.com".GetJsonAsync<object>();
Assert.Fail("should have failed to parse response.");
}
catch (FlurlParsingException ex) {

View File

@ -42,31 +42,6 @@ namespace Flurl.Test.Http
Assert.AreEqual("Frank", data.name);
}
[Test]
public async Task can_get_json_dynamic() {
HttpTest.RespondWithJson(new { id = 1, name = "Frank" });
var data = await "http://some-api.com".GetJsonAsync();
Assert.AreEqual(1, data.id);
Assert.AreEqual("Frank", data.name);
}
[Test]
public async Task can_get_json_dynamic_list() {
HttpTest.RespondWithJson(new[] {
new { id = 1, name = "Frank" },
new { id = 2, name = "Claire" }
});
var data = await "http://some-api.com".GetJsonListAsync();
Assert.AreEqual(1, data[0].id);
Assert.AreEqual("Frank", data[0].name);
Assert.AreEqual(2, data[1].id);
Assert.AreEqual("Claire", data[1].name);
}
[Test]
public async Task can_get_string() {
HttpTest.RespondWith("good job");
@ -114,7 +89,7 @@ namespace Flurl.Test.Http
[TestCase(false)]
[TestCase(true)]
public async Task can_get_error_json_typed(bool useShortcut) {
public async Task can_get_error_json(bool useShortcut) {
HttpTest.RespondWithJson(new { code = 999, message = "our server crashed" }, 500);
try {
@ -130,24 +105,6 @@ namespace Flurl.Test.Http
}
}
[TestCase(false)]
[TestCase(true)]
public async Task can_get_error_json_untyped(bool useShortcut) {
HttpTest.RespondWithJson(new { code = 999, message = "our server crashed" }, 500);
try {
await "http://api.com".GetStringAsync();
}
catch (FlurlHttpException ex) {
var error = useShortcut ? // error is a dynamic this time
await ex.GetResponseJsonAsync() :
await ex.Call.Response.GetJsonAsync();
Assert.IsNotNull(error);
Assert.AreEqual(999, error.code);
Assert.AreEqual("our server crashed", error.message);
}
}
[Test]
public async Task can_get_null_json_when_timeout_and_exception_handled() {
HttpTest.SimulateTimeout();

View File

@ -66,31 +66,6 @@ namespace Flurl.Test.Http
Assert.AreEqual("Frank", data.name);
}
[Test]
public async Task can_receive_json_dynamic() {
HttpTest.RespondWithJson(new { id = 1, name = "Frank" });
var data = await "http://some-api.com".PostJsonAsync(new { a = 1, b = 2 }).ReceiveJson();
Assert.AreEqual(1, data.id);
Assert.AreEqual("Frank", data.name);
}
[Test]
public async Task can_receive_json_dynamic_list() {
HttpTest.RespondWithJson(new[] {
new { id = 1, name = "Frank" },
new { id = 2, name = "Claire" }
});
var data = await "http://some-api.com".PostJsonAsync(new { a = 1, b = 2 }).ReceiveJsonList();
Assert.AreEqual(1, data[0].id);
Assert.AreEqual("Frank", data[0].name);
Assert.AreEqual(2, data[1].id);
Assert.AreEqual("Claire", data[1].name);
}
[Test]
public async Task can_receive_string() {
HttpTest.RespondWith("good job");

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Flurl.Http;
@ -18,6 +19,15 @@ namespace Flurl.Test.Http
[TestFixture, Parallelizable]
public class RealHttpTests
{
public class HttpBinResponse
{
public Dictionary<string, JsonElement> json { get; set; }
public Dictionary<string, string> args { get; set; }
public Dictionary<string, string> form { get; set; }
public Dictionary<string, string> cookies { get; set; }
public Dictionary<string, string> files { get; set; }
}
[TestCase("gzip", "gzipped")]
[TestCase("deflate", "deflated"), Ignore("#474")]
public async Task decompresses_automatically(string encoding, string jsonKey) {
@ -54,9 +64,9 @@ namespace Flurl.Test.Http
[Test]
public async Task can_post_and_receive_json() {
var result = await "https://httpbin.org/post".PostJsonAsync(new { a = 1, b = 2 }).ReceiveJson();
Assert.AreEqual(result.json.a, 1);
Assert.AreEqual(result.json.b, 2);
var result = await "https://httpbin.org/post".PostJsonAsync(new { a = 1, b = 2 }).ReceiveJson<HttpBinResponse>();
Assert.AreEqual(1, result.json["a"].GetInt32());
Assert.AreEqual(2, result.json["b"].GetInt32());
}
[Test]
@ -115,12 +125,12 @@ namespace Flurl.Test.Http
// content.Headers.ContentLength = 735;
})
//.ReceiveString();
.ReceiveJson();
Assert.AreEqual("1", resp.form.a);
Assert.AreEqual("2", resp.form.b);
Assert.AreEqual("hello!", resp.form.DataField);
Assert.AreEqual("file contents 1", resp.files.File1);
Assert.AreEqual("file contents 2", resp.files.File2);
.ReceiveJson<HttpBinResponse>();
Assert.AreEqual("1", resp.form["a"]);
Assert.AreEqual("2", resp.form["b"]);
Assert.AreEqual("hello!", resp.form["DataField"]);
Assert.AreEqual("file contents 1", resp.files["File1"]);
Assert.AreEqual("file contents 2", resp.files["File2"]);
}
}
finally {
@ -138,7 +148,7 @@ namespace Flurl.Test.Http
call.ExceptionHandled = true;
handlerCalled = true;
};
}).GetJsonAsync();
}).GetAsync();
Assert.IsTrue(handlerCalled, "error handler should have been called.");
}
catch (FlurlHttpException) {
@ -156,7 +166,7 @@ namespace Flurl.Test.Http
ex = call.Exception;
call.ExceptionHandled = true;
};
}).GetJsonAsync();
}).GetJsonAsync<object>();
Assert.IsNotNull(ex, "error handler should have been called.");
Assert.IsInstanceOf<FlurlParsingException>(ex);
}
@ -288,7 +298,7 @@ namespace Flurl.Test.Http
Assert.AreEqual("foo", await "https://www.google.com".GetStringAsync());
Assert.AreNotEqual("foo", await "https://httpbin.org/get".GetStringAsync());
Assert.AreEqual("bar", (await "https://httpbin.org/get?x=bar".GetJsonAsync()).args.x);
Assert.AreEqual("bar", (await "https://httpbin.org/get?x=bar".GetJsonAsync<HttpBinResponse>()).args["x"]);
Assert.AreEqual("foo", await "https://www.microsoft.com".GetStringAsync());
// real calls still get logged
@ -324,10 +334,10 @@ namespace Flurl.Test.Http
var s = await req.GetStringAsync();
var resp = await req.WithAutoRedirect(false).GetJsonAsync();
var resp = await req.WithAutoRedirect(false).GetJsonAsync<HttpBinResponse>();
// httpbin returns json representation of cookies that were sent
Assert.AreEqual("1", resp.cookies.x);
Assert.AreEqual("2", resp.cookies.y);
Assert.AreEqual("1", resp.cookies["x"]);
Assert.AreEqual("2", resp.cookies["y"]);
}
[Test]
@ -352,8 +362,8 @@ namespace Flurl.Test.Http
public async Task can_set_cookies_before_setting_url() {
var req = new FlurlRequest().WithCookie("z", "999");
req.Url = "https://httpbin.org/cookies";
var resp = await req.GetJsonAsync();
Assert.AreEqual("999", resp.cookies.z);
var resp = await req.GetJsonAsync<HttpBinResponse>();
Assert.AreEqual("999", resp.cookies["z"]);
}
[Test]
@ -363,19 +373,22 @@ namespace Flurl.Test.Http
var req1 = cli.Request("https://httpbin.org/cookies").WithCookie("x", "123");
var req2 = cli.Request("https://httpbin.org/cookies").WithCookie("x", "abc");
var resp2 = await req2.GetJsonAsync();
var resp1 = await req1.GetJsonAsync();
var resp2 = await req2.GetJsonAsync<HttpBinResponse>();
var resp1 = await req1.GetJsonAsync<HttpBinResponse>();
Assert.AreEqual("123", resp1.cookies.x);
Assert.AreEqual("abc", resp2.cookies.x);
Assert.AreEqual("123", resp1.cookies["x"]);
Assert.AreEqual("abc", resp2.cookies["x"]);
}
[Test]
public async Task can_receive_cookie_from_redirect_response_and_add_it_to_jar() {
// use httpbingo instead of httpbin because of redirect issue https://github.com/postmanlabs/httpbin/issues/617
var resp = await "https://httpbingo.org/redirect-to".SetQueryParam("url", "/cookies/set?x=foo").WithCookies(out var jar).GetJsonAsync();
var resp = await "https://httpbingo.org/redirect-to"
.SetQueryParam("url", "/cookies/set?x=foo")
.WithCookies(out var jar)
.GetJsonAsync<Dictionary<string, string>>();
Assert.AreEqual("foo", resp.x);
Assert.AreEqual("foo", resp["x"]);
Assert.AreEqual(1, jar.Count);
}
#endregion

View File

@ -1,7 +1,6 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Flurl.CodeGen
{

View File

@ -1,6 +1,3 @@
using System.Collections.Generic;
using System.Linq;
namespace Flurl.CodeGen
{
public class HttpExtensionMethod : ExtensionMethod
@ -44,31 +41,20 @@ namespace Flurl.CodeGen
public string RequestBodyType { get; }
public string ResponseBodyType { get; }
public string TaskArg {
get {
switch (ResponseBodyType) {
case "Json": return IsGeneric ? "T" : "dynamic";
case "JsonList": return "IList<dynamic>";
case "String": return "string";
case "Stream": return "Stream";
case "Bytes": return "byte[]";
default: return "IFlurlResponse";
}
}
}
public string TaskArg => ResponseBodyType switch {
"Json" => "T",
"String" => "string",
"Stream" => "Stream",
"Bytes" => "byte[]",
_ => "IFlurlResponse"
};
public string ReturnTypeDescription {
get {
//var response = (xm.DeserializeToType == null) ? "" : "" + xm.TaskArg;
switch (ResponseBodyType) {
case "Json": return "the JSON response body deserialized to " + (IsGeneric ? "an object of type T" : "a dynamic");
case "JsonList": return "the JSON response body deserialized to a list of dynamics";
case "String": return "the response body as a string";
case "Stream": return "the response body as a Stream";
case "Bytes": return "the response body as a byte array";
default: return "the received IFlurlResponse";
}
}
}
public string ReturnTypeDescription => ResponseBodyType switch {
"Json" => "the JSON response body deserialized to an object of type T",
"String" => "the response body as a string",
"Stream" => "the response body as a Stream",
"Bytes" => "the response body as a byte array",
_ => "the received IFlurlResponse"
};
}
}

View File

@ -144,16 +144,13 @@ namespace Flurl.CodeGen
/// HTTP-calling methods for all valid verb/content type combinations (PutStringAsync, PatchJsonAsync, etc),
/// with generic and non-generic overloads.
/// </summary>
public static IEnumerable<HttpExtensionMethod> GetHttpCallingExtensions(MethodArg extendedArg) {
return
from verb in new[] { null, "Get", "Post", "Head", "Put", "Delete", "Patch", "Options" }
from reqType in new[] { null, "Json", "String", "UrlEncoded" }
from respType in new[] { null, "Json", "JsonList", "String", "Stream", "Bytes" }
where IsSupportedCombo(verb, reqType, respType, extendedArg.Type)
from isGeneric in new[] { true, false }
where !isGeneric || AllowDeserializeToGeneric(respType)
select new HttpExtensionMethod(verb, isGeneric, reqType, respType) { ExtendedTypeArg = extendedArg };
}
public static IEnumerable<HttpExtensionMethod> GetHttpCallingExtensions(MethodArg extendedArg) =>
from verb in new[] { null, "Get", "Post", "Head", "Put", "Delete", "Patch", "Options" }
from reqType in new[] { null, "Json", "String", "UrlEncoded" }
from respType in new[] { null, "Json", "String", "Stream", "Bytes" }
where IsSupportedCombo(verb, reqType, respType, extendedArg.Type)
let isGenenric = (respType == "Json")
select new HttpExtensionMethod(verb, isGenenric, reqType, respType) { ExtendedTypeArg = extendedArg };
private static bool IsSupportedCombo(string verb, string reqType, string respType, string extensionType) {
if (respType != null && verb != "Get")
@ -171,14 +168,5 @@ namespace Flurl.CodeGen
return reqType == null;
}
}
private static bool AllowDeserializeToGeneric(string deserializeType) {
switch (deserializeType) {
case "Json":
return true;
default:
return false;
}
}
}
}

View File

@ -50,7 +50,6 @@ namespace Flurl.CodeGen
writer
.WriteLine("// This file was auto-generated by Flurl.CodeGen. Do not edit directly.")
.WriteLine("using System;")
.WriteLine("using System.Collections.Generic;")
.WriteLine("using System.IO;")
.WriteLine("using System.Net;")
.WriteLine("using System.Net.Http;")

View File

@ -1,7 +1,5 @@
using System;
using System.Dynamic;
using System.Threading.Tasks;
using Flurl.Http;
namespace Flurl.Http
{
@ -65,13 +63,6 @@ namespace Flurl.Http
/// <typeparam name="T">A type whose structure matches the expected JSON response.</typeparam>
/// <returns>A task whose result is an object containing data in the response body.</returns>
public Task<T> GetResponseJsonAsync<T>() => Call?.Response?.GetJsonAsync<T>() ?? Task.FromResult(default(T));
/// <summary>
/// Deserializes the JSON response body to a dynamic object.
/// </summary>
/// <returns>A task whose result is an object containing data in the response body.</returns>
public async Task<dynamic> GetResponseJsonAsync() => (Call?.Response == null) ? null :
await Call.Response.GetJsonAsync().ConfigureAwait(false);
}
/// <summary>

View File

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.IO;
using System.Linq;
using System.Net.Http;
@ -45,22 +44,6 @@ namespace Flurl.Http
/// <exception cref="FlurlHttpException">Condition.</exception>
Task<T> GetJsonAsync<T>();
/// <summary>
/// Deserializes JSON-formatted HTTP response body to a dynamic object.
/// </summary>
/// <returns>A Task whose result is a dynamic object containing data in the response body.</returns>
/// <example>d = await url.PostAsync(data).GetJson()</example>
/// <exception cref="FlurlHttpException">Condition.</exception>
Task<dynamic> GetJsonAsync();
/// <summary>
/// Deserializes JSON-formatted HTTP response body to a list of dynamic objects.
/// </summary>
/// <returns>A Task whose result is a list of dynamic objects containing data in the response body.</returns>
/// <example>d = await url.PostAsync(data).GetJsonList()</example>
/// <exception cref="FlurlHttpException">Condition.</exception>
Task<IList<dynamic>> GetJsonListAsync();
/// <summary>
/// Returns HTTP response body as a string.
/// </summary>
@ -182,18 +165,6 @@ namespace Flurl.Http
}
}
/// <inheritdoc />
public async Task<dynamic> GetJsonAsync() {
dynamic d = await GetJsonAsync<ExpandoObject>().ConfigureAwait(false);
return d;
}
/// <inheritdoc />
public async Task<IList<dynamic>> GetJsonListAsync() {
dynamic[] d = await GetJsonAsync<ExpandoObject[]>().ConfigureAwait(false);
return d;
}
/// <inheritdoc />
public async Task<string> GetStringAsync() {
if (_streamRead) {

View File

@ -1,6 +1,5 @@
// This file was auto-generated by Flurl.CodeGen. Do not edit directly.
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
@ -80,28 +79,6 @@ namespace Flurl.Http
return request.SendAsync(HttpMethod.Get, cancellationToken: cancellationToken, completionOption: completionOption).ReceiveJson<T>();
}
/// <summary>
/// Sends an asynchronous GET request.
/// </summary>
/// <param name="request">This IFlurlRequest</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <param name="completionOption">The HttpCompletionOption used in the request. Optional.</param>
/// <returns>A Task whose result is the JSON response body deserialized to a dynamic.</returns>
public static Task<dynamic> GetJsonAsync(this IFlurlRequest request, CancellationToken cancellationToken = default(CancellationToken), HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead) {
return request.SendAsync(HttpMethod.Get, cancellationToken: cancellationToken, completionOption: completionOption).ReceiveJson();
}
/// <summary>
/// Sends an asynchronous GET request.
/// </summary>
/// <param name="request">This IFlurlRequest</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <param name="completionOption">The HttpCompletionOption used in the request. Optional.</param>
/// <returns>A Task whose result is the JSON response body deserialized to a list of dynamics.</returns>
public static Task<IList<dynamic>> GetJsonListAsync(this IFlurlRequest request, CancellationToken cancellationToken = default(CancellationToken), HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead) {
return request.SendAsync(HttpMethod.Get, cancellationToken: cancellationToken, completionOption: completionOption).ReceiveJsonList();
}
/// <summary>
/// Sends an asynchronous GET request.
/// </summary>
@ -369,28 +346,6 @@ namespace Flurl.Http
return new FlurlRequest(url).GetJsonAsync<T>(cancellationToken, completionOption);
}
/// <summary>
/// Creates a FlurlRequest and sends an asynchronous GET request.
/// </summary>
/// <param name="url">This Flurl.Url.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <param name="completionOption">The HttpCompletionOption used in the request. Optional.</param>
/// <returns>A Task whose result is the JSON response body deserialized to a dynamic.</returns>
public static Task<dynamic> GetJsonAsync(this Url url, CancellationToken cancellationToken = default(CancellationToken), HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead) {
return new FlurlRequest(url).GetJsonAsync(cancellationToken, completionOption);
}
/// <summary>
/// Creates a FlurlRequest and sends an asynchronous GET request.
/// </summary>
/// <param name="url">This Flurl.Url.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <param name="completionOption">The HttpCompletionOption used in the request. Optional.</param>
/// <returns>A Task whose result is the JSON response body deserialized to a list of dynamics.</returns>
public static Task<IList<dynamic>> GetJsonListAsync(this Url url, CancellationToken cancellationToken = default(CancellationToken), HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead) {
return new FlurlRequest(url).GetJsonListAsync(cancellationToken, completionOption);
}
/// <summary>
/// Creates a FlurlRequest and sends an asynchronous GET request.
/// </summary>
@ -838,28 +793,6 @@ namespace Flurl.Http
return new FlurlRequest(url).GetJsonAsync<T>(cancellationToken, completionOption);
}
/// <summary>
/// Creates a FlurlRequest and sends an asynchronous GET request.
/// </summary>
/// <param name="url">This URL.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <param name="completionOption">The HttpCompletionOption used in the request. Optional.</param>
/// <returns>A Task whose result is the JSON response body deserialized to a dynamic.</returns>
public static Task<dynamic> GetJsonAsync(this string url, CancellationToken cancellationToken = default(CancellationToken), HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead) {
return new FlurlRequest(url).GetJsonAsync(cancellationToken, completionOption);
}
/// <summary>
/// Creates a FlurlRequest and sends an asynchronous GET request.
/// </summary>
/// <param name="url">This URL.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <param name="completionOption">The HttpCompletionOption used in the request. Optional.</param>
/// <returns>A Task whose result is the JSON response body deserialized to a list of dynamics.</returns>
public static Task<IList<dynamic>> GetJsonListAsync(this string url, CancellationToken cancellationToken = default(CancellationToken), HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead) {
return new FlurlRequest(url).GetJsonListAsync(cancellationToken, completionOption);
}
/// <summary>
/// Creates a FlurlRequest and sends an asynchronous GET request.
/// </summary>
@ -1307,28 +1240,6 @@ namespace Flurl.Http
return new FlurlRequest(uri).GetJsonAsync<T>(cancellationToken, completionOption);
}
/// <summary>
/// Creates a FlurlRequest and sends an asynchronous GET request.
/// </summary>
/// <param name="uri">This System.Uri.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <param name="completionOption">The HttpCompletionOption used in the request. Optional.</param>
/// <returns>A Task whose result is the JSON response body deserialized to a dynamic.</returns>
public static Task<dynamic> GetJsonAsync(this Uri uri, CancellationToken cancellationToken = default(CancellationToken), HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead) {
return new FlurlRequest(uri).GetJsonAsync(cancellationToken, completionOption);
}
/// <summary>
/// Creates a FlurlRequest and sends an asynchronous GET request.
/// </summary>
/// <param name="uri">This System.Uri.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <param name="completionOption">The HttpCompletionOption used in the request. Optional.</param>
/// <returns>A Task whose result is the JSON response body deserialized to a list of dynamics.</returns>
public static Task<IList<dynamic>> GetJsonListAsync(this Uri uri, CancellationToken cancellationToken = default(CancellationToken), HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead) {
return new FlurlRequest(uri).GetJsonListAsync(cancellationToken, completionOption);
}
/// <summary>
/// Creates a FlurlRequest and sends an asynchronous GET request.
/// </summary>

View File

@ -1,8 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace Flurl.Http
@ -27,32 +24,6 @@ namespace Flurl.Http
}
}
/// <summary>
/// Deserializes JSON-formatted HTTP response body to a dynamic object. Intended to chain off an async call.
/// </summary>
/// <returns>A Task whose result is a dynamic object containing data in the response body.</returns>
/// <example>d = await url.PostAsync(data).ReceiveJson()</example>
/// <exception cref="FlurlHttpException">Condition.</exception>
public static async Task<dynamic> ReceiveJson(this Task<IFlurlResponse> response) {
using (var resp = await response.ConfigureAwait(false)) {
if (resp == null) return null;
return await resp.GetJsonAsync().ConfigureAwait(false);
}
}
/// <summary>
/// Deserializes JSON-formatted HTTP response body to a list of dynamic objects. Intended to chain off an async call.
/// </summary>
/// <returns>A Task whose result is a list of dynamic objects containing data in the response body.</returns>
/// <example>d = await url.PostAsync(data).ReceiveJsonList()</example>
/// <exception cref="FlurlHttpException">Condition.</exception>
public static async Task<IList<dynamic>> ReceiveJsonList(this Task<IFlurlResponse> response) {
using (var resp = await response.ConfigureAwait(false)) {
if (resp == null) return null;
return await resp.GetJsonListAsync().ConfigureAwait(false);
}
}
/// <summary>
/// Returns HTTP response body as a string. Intended to chain off an async call.
/// </summary>