Skip to content

Commit

Permalink
#699 no dynamic support in STJ, none in Flurl now either
Browse files Browse the repository at this point in the history
  • Loading branch information
tmenier committed Jun 3, 2022
1 parent a53f46a commit ac6b55e
Show file tree
Hide file tree
Showing 12 changed files with 61 additions and 300 deletions.
2 changes: 1 addition & 1 deletion Test/Flurl.Test/Http/FlurlHttpExceptionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public async Task can_catch_parsing_error() {
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) {
Expand Down
45 changes: 1 addition & 44 deletions Test/Flurl.Test/Http/GetTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,31 +42,6 @@ public async Task can_get_response_then_deserialize() {
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");
Expand Down Expand Up @@ -114,7 +89,7 @@ public async Task failure_throws_detailed_exception() {

[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 {
Expand All @@ -130,24 +105,6 @@ await ex.GetResponseJsonAsync<TestError>() :
}
}

[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();
Expand Down
25 changes: 0 additions & 25 deletions Test/Flurl.Test/Http/PostTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,31 +66,6 @@ public async Task can_receive_json() {
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");
Expand Down
59 changes: 36 additions & 23 deletions Test/Flurl.Test/Http/RealHttpTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Flurl.Http;
Expand All @@ -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) {
Expand Down Expand Up @@ -54,9 +64,9 @@ public async Task can_download_file(string url, string contentDisposition, strin

[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]
Expand Down Expand Up @@ -115,12 +125,12 @@ public async Task can_post_multipart() {
// 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 {
Expand All @@ -138,7 +148,7 @@ public async Task can_handle_http_error() {
call.ExceptionHandled = true;
handlerCalled = true;
};
}).GetJsonAsync();
}).GetAsync();
Assert.IsTrue(handlerCalled, "error handler should have been called.");
}
catch (FlurlHttpException) {
Expand All @@ -156,7 +166,7 @@ public async Task can_handle_parsing_error() {
ex = call.Exception;
call.ExceptionHandled = true;
};
}).GetJsonAsync();
}).GetJsonAsync<object>();
Assert.IsNotNull(ex, "error handler should have been called.");
Assert.IsInstanceOf<FlurlParsingException>(ex);
}
Expand Down Expand Up @@ -288,7 +298,7 @@ public async Task can_allow_real_http_in_test() {

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
Expand Down Expand Up @@ -324,10 +334,10 @@ public async Task can_send_cookies() {

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]
Expand All @@ -352,8 +362,8 @@ public async Task can_receive_cookies() {
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]
Expand All @@ -363,19 +373,22 @@ public async Task can_send_different_cookies_per_request() {
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
Expand Down
3 changes: 1 addition & 2 deletions src/Flurl.CodeGen/ExtensionMethod.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Flurl.CodeGen
{
Expand Down
44 changes: 15 additions & 29 deletions src/Flurl.CodeGen/HttpExtensionMethod.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
using System.Collections.Generic;
using System.Linq;

namespace Flurl.CodeGen
{
public class HttpExtensionMethod : ExtensionMethod
Expand Down Expand Up @@ -44,31 +41,20 @@ public HttpExtensionMethod(string verb, bool isGeneric, string reqBodyType, stri
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 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 TaskArg => ResponseBodyType switch {
"Json" => "T",
"String" => "string",
"Stream" => "Stream",
"Bytes" => "byte[]",
_ => "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"
};
}
}
26 changes: 7 additions & 19 deletions src/Flurl.CodeGen/Metadata.cs
Original file line number Diff line number Diff line change
Expand Up @@ -144,16 +144,13 @@ public static IEnumerable<ExtensionMethod> GetRequestReturningExtensions(MethodA
/// 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")
Expand All @@ -171,14 +168,5 @@ private static bool IsSupportedCombo(string verb, string reqType, string respTyp
return reqType == null;
}
}

private static bool AllowDeserializeToGeneric(string deserializeType) {
switch (deserializeType) {
case "Json":
return true;
default:
return false;
}
}
}
}
1 change: 0 additions & 1 deletion src/Flurl.CodeGen/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ static int Main(string[] args) {
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;")
Expand Down
9 changes: 0 additions & 9 deletions src/Flurl.Http/FlurlHttpException.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
using System;
using System.Dynamic;
using System.Threading.Tasks;
using Flurl.Http;

namespace Flurl.Http
{
Expand Down Expand Up @@ -65,13 +63,6 @@ private static string BuildMessage(FlurlCall call, Exception inner) {
/// <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>
Expand Down
Loading

0 comments on commit ac6b55e

Please sign in to comment.