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

Fix the FromServices attribute #93

Merged
merged 1 commit into from
Jan 23, 2020
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
Expand Up @@ -29,9 +29,15 @@ public ParameterDefinition(ParameterSyntax parameter, HttpRoute fullRoute)
return;
}


var attributes = parameter.AttributeLists.SelectMany(x => x.Attributes).ToList();

if (attributes.Any(x => x.Name.ToFullString().MatchesAttribute(nameof(FromServicesAttribute))))
{
Invalid = true;
return;
}


Options = new ParameterAttributeOptions
{
FromQuery = attributes.Any(x => x.Name.ToFullString().MatchesAttribute(nameof(FromQueryAttribute))),
Expand Down
250 changes: 250 additions & 0 deletions tests/TestWebApp.Clients/Clients.cs
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,14 @@ public static string CancellationTokenApi()
string url = $@"api/{controller}/{action}";
return url;
}

public static string TestFromServicesAttribute()
{
var controller = "Values";
var action = "TestFromServicesAttribute";
string url = $@"api/{controller}/{action}";
return url;
}
}
}

Expand Down Expand Up @@ -2516,6 +2524,10 @@ public interface IValuesClient : ITestWebAppClient
HttpResponseMessage CancellationTokenApiRaw(int ControllerHeader = 0, Action<FlurlHttpException> ExceptionCallback = null, IDictionary<String, Object> headers = null, IEnumerable<Cookie> cookies = null, TimeSpan? timeout = null, CancellationToken cancellationToken = default);
Task CancellationTokenApiAsync(int ControllerHeader = 0, Action<string> BadRequestCallback = null, Action InternalServerErrorCallback = null, Action OKCallback = null, Action<HttpResponseMessage> ResponseCallback = null, Action<FlurlHttpException> ExceptionCallback = null, IDictionary<String, Object> headers = null, IEnumerable<Cookie> cookies = null, TimeSpan? timeout = null, CancellationToken cancellationToken = default);
ValueTask<HttpResponseMessage> CancellationTokenApiRawAsync(int ControllerHeader = 0, Action<FlurlHttpException> ExceptionCallback = null, IDictionary<String, Object> headers = null, IEnumerable<Cookie> cookies = null, TimeSpan? timeout = null, CancellationToken cancellationToken = default);
void TestFromServicesAttribute(int ControllerHeader = 0, Action<string> BadRequestCallback = null, Action InternalServerErrorCallback = null, Action<HttpResponseMessage> ResponseCallback = null, Action<FlurlHttpException> ExceptionCallback = null, IDictionary<String, Object> headers = null, IEnumerable<Cookie> cookies = null, TimeSpan? timeout = null, CancellationToken cancellationToken = default);
HttpResponseMessage TestFromServicesAttributeRaw(int ControllerHeader = 0, Action<FlurlHttpException> ExceptionCallback = null, IDictionary<String, Object> headers = null, IEnumerable<Cookie> cookies = null, TimeSpan? timeout = null, CancellationToken cancellationToken = default);
Task TestFromServicesAttributeAsync(int ControllerHeader = 0, Action<string> BadRequestCallback = null, Action InternalServerErrorCallback = null, Action<HttpResponseMessage> ResponseCallback = null, Action<FlurlHttpException> ExceptionCallback = null, IDictionary<String, Object> headers = null, IEnumerable<Cookie> cookies = null, TimeSpan? timeout = null, CancellationToken cancellationToken = default);
ValueTask<HttpResponseMessage> TestFromServicesAttributeRawAsync(int ControllerHeader = 0, Action<FlurlHttpException> ExceptionCallback = null, IDictionary<String, Object> headers = null, IEnumerable<Cookie> cookies = null, TimeSpan? timeout = null, CancellationToken cancellationToken = default);
}

internal class ValuesClient : IValuesClient
Expand Down Expand Up @@ -13687,6 +13699,244 @@ public async ValueTask<HttpResponseMessage> CancellationTokenApiRawAsync(int Con

return response;
}

public void TestFromServicesAttribute(int ControllerHeader = 0, Action<string> BadRequestCallback = null, Action InternalServerErrorCallback = null, Action<HttpResponseMessage> ResponseCallback = null, Action<FlurlHttpException> ExceptionCallback = null, IDictionary<String, Object> headers = null, IEnumerable<Cookie> cookies = null, TimeSpan? timeout = null, CancellationToken cancellationToken = default)
{
var controller = "Values";
var action = "TestFromServicesAttribute";
string url = $@"api/{controller}/{action}";
HttpResponseMessage response = null;
response = HttpOverride.GetResponseAsync(HttpMethod.Get, url, null, cancellationToken).ConfigureAwait(false).GetAwaiter().GetResult();
bool responseHandled = response != null;
if (response == null)
{
try
{
response = Client.ClientWrapper.Request(url).WithHeader("Test", "EXTRA").WithHeader("ControllerHeader", ControllerHeader).WithRequestModifiers(Modifier).WithCookies(cookies).WithHeaders(headers).WithTimeout(timeout ?? Client.Timeout).AllowAnyHttpStatus().GetAsync(cancellationToken).ConfigureAwait(false).GetAwaiter().GetResult();
}
catch (FlurlHttpException fhex)
{
if (ExceptionCallback != null && ExceptionCallback.Method.IsDefined(typeof(AsyncStateMachineAttribute), true))
{
throw new NotSupportedException("Async void action delegates for ExceptionCallback are not supported.As they will run out of the scope of this call.");
}

if (ExceptionCallback != null)
{
responseHandled = true;
ExceptionCallback?.Invoke(fhex);
}
else
{
throw fhex;
}

return;
}

HttpOverride.OnNonOverridedResponseAsync(HttpMethod.Get, url, null, response, cancellationToken).ConfigureAwait(false).GetAwaiter().GetResult();
}

if (BadRequestCallback != null && BadRequestCallback.Method.IsDefined(typeof(AsyncStateMachineAttribute), true))
{
throw new NotSupportedException("Async void action delegates for BadRequestCallback are not supported.As they will run out of the scope of this call.");
}

if (response.StatusCode == System.Net.HttpStatusCode.BadRequest)
{
if (BadRequestCallback != null)
{
responseHandled = true;
BadRequestCallback.Invoke(Serializer.Deserialize<string>(response.Content).ConfigureAwait(false).GetAwaiter().GetResult());
}
}

if (InternalServerErrorCallback != null && InternalServerErrorCallback.Method.IsDefined(typeof(AsyncStateMachineAttribute), true))
{
throw new NotSupportedException("Async void action delegates for InternalServerErrorCallback are not supported.As they will run out of the scope of this call.");
}

if (response.StatusCode == System.Net.HttpStatusCode.InternalServerError)
{
if (InternalServerErrorCallback != null)
{
responseHandled = true;
InternalServerErrorCallback.Invoke();
}
}

if (ResponseCallback != null && ResponseCallback.Method.IsDefined(typeof(AsyncStateMachineAttribute), true))
{
throw new NotSupportedException("Async void action delegates for ResponseCallback are not supported.As they will run out of the scope of this call.");
}

if (ResponseCallback != null)
{
responseHandled = true;
ResponseCallback.Invoke(response);
}

return;
}

public HttpResponseMessage TestFromServicesAttributeRaw(int ControllerHeader = 0, Action<FlurlHttpException> ExceptionCallback = null, IDictionary<String, Object> headers = null, IEnumerable<Cookie> cookies = null, TimeSpan? timeout = null, CancellationToken cancellationToken = default)
{
var controller = "Values";
var action = "TestFromServicesAttribute";
string url = $@"api/{controller}/{action}";
HttpResponseMessage response = null;
response = HttpOverride.GetResponseAsync(HttpMethod.Get, url, null, cancellationToken).ConfigureAwait(false).GetAwaiter().GetResult();
bool responseHandled = response != null;
if (response == null)
{
try
{
response = Client.ClientWrapper.Request(url).WithHeader("Test", "EXTRA").WithHeader("ControllerHeader", ControllerHeader).WithRequestModifiers(Modifier).WithCookies(cookies).WithHeaders(headers).WithTimeout(timeout ?? Client.Timeout).AllowAnyHttpStatus().GetAsync(cancellationToken).ConfigureAwait(false).GetAwaiter().GetResult();
}
catch (FlurlHttpException fhex)
{
if (ExceptionCallback != null && ExceptionCallback.Method.IsDefined(typeof(AsyncStateMachineAttribute), true))
{
throw new NotSupportedException("Async void action delegates for ExceptionCallback are not supported.As they will run out of the scope of this call.");
}

if (ExceptionCallback != null)
{
responseHandled = true;
ExceptionCallback?.Invoke(fhex);
}
else
{
throw fhex;
}

return null;
}

HttpOverride.OnNonOverridedResponseAsync(HttpMethod.Get, url, null, response, cancellationToken).ConfigureAwait(false).GetAwaiter().GetResult();
}

return response;
}

public async Task TestFromServicesAttributeAsync(int ControllerHeader = 0, Action<string> BadRequestCallback = null, Action InternalServerErrorCallback = null, Action<HttpResponseMessage> ResponseCallback = null, Action<FlurlHttpException> ExceptionCallback = null, IDictionary<String, Object> headers = null, IEnumerable<Cookie> cookies = null, TimeSpan? timeout = null, CancellationToken cancellationToken = default)
{
var controller = "Values";
var action = "TestFromServicesAttribute";
string url = $@"api/{controller}/{action}";
HttpResponseMessage response = null;
response = await HttpOverride.GetResponseAsync(HttpMethod.Get, url, null, cancellationToken).ConfigureAwait(false);
bool responseHandled = response != null;
if (response == null)
{
try
{
response = await Client.ClientWrapper.Request(url).WithHeader("Test", "EXTRA").WithHeader("ControllerHeader", ControllerHeader).WithRequestModifiers(Modifier).WithCookies(cookies).WithHeaders(headers).WithTimeout(timeout ?? Client.Timeout).AllowAnyHttpStatus().GetAsync(cancellationToken).ConfigureAwait(false);
}
catch (FlurlHttpException fhex)
{
if (ExceptionCallback != null && ExceptionCallback.Method.IsDefined(typeof(AsyncStateMachineAttribute), true))
{
throw new NotSupportedException("Async void action delegates for ExceptionCallback are not supported.As they will run out of the scope of this call.");
}

if (ExceptionCallback != null)
{
responseHandled = true;
ExceptionCallback?.Invoke(fhex);
}
else
{
throw fhex;
}

return;
}

await HttpOverride.OnNonOverridedResponseAsync(HttpMethod.Get, url, null, response, cancellationToken).ConfigureAwait(false);
}

if (BadRequestCallback != null && BadRequestCallback.Method.IsDefined(typeof(AsyncStateMachineAttribute), true))
{
throw new NotSupportedException("Async void action delegates for BadRequestCallback are not supported.As they will run out of the scope of this call.");
}

if (response.StatusCode == System.Net.HttpStatusCode.BadRequest)
{
if (BadRequestCallback != null)
{
responseHandled = true;
BadRequestCallback.Invoke(await Serializer.Deserialize<string>(response.Content).ConfigureAwait(false));
}
}

if (InternalServerErrorCallback != null && InternalServerErrorCallback.Method.IsDefined(typeof(AsyncStateMachineAttribute), true))
{
throw new NotSupportedException("Async void action delegates for InternalServerErrorCallback are not supported.As they will run out of the scope of this call.");
}

if (response.StatusCode == System.Net.HttpStatusCode.InternalServerError)
{
if (InternalServerErrorCallback != null)
{
responseHandled = true;
InternalServerErrorCallback.Invoke();
}
}

if (ResponseCallback != null && ResponseCallback.Method.IsDefined(typeof(AsyncStateMachineAttribute), true))
{
throw new NotSupportedException("Async void action delegates for ResponseCallback are not supported.As they will run out of the scope of this call.");
}

if (ResponseCallback != null)
{
responseHandled = true;
ResponseCallback.Invoke(response);
}

return;
}

public async ValueTask<HttpResponseMessage> TestFromServicesAttributeRawAsync(int ControllerHeader = 0, Action<FlurlHttpException> ExceptionCallback = null, IDictionary<String, Object> headers = null, IEnumerable<Cookie> cookies = null, TimeSpan? timeout = null, CancellationToken cancellationToken = default)
{
var controller = "Values";
var action = "TestFromServicesAttribute";
string url = $@"api/{controller}/{action}";
HttpResponseMessage response = null;
response = await HttpOverride.GetResponseAsync(HttpMethod.Get, url, null, cancellationToken).ConfigureAwait(false);
bool responseHandled = response != null;
if (response == null)
{
try
{
response = await Client.ClientWrapper.Request(url).WithHeader("Test", "EXTRA").WithHeader("ControllerHeader", ControllerHeader).WithRequestModifiers(Modifier).WithCookies(cookies).WithHeaders(headers).WithTimeout(timeout ?? Client.Timeout).AllowAnyHttpStatus().GetAsync(cancellationToken).ConfigureAwait(false);
}
catch (FlurlHttpException fhex)
{
if (ExceptionCallback != null && ExceptionCallback.Method.IsDefined(typeof(AsyncStateMachineAttribute), true))
{
throw new NotSupportedException("Async void action delegates for ExceptionCallback are not supported.As they will run out of the scope of this call.");
}

if (ExceptionCallback != null)
{
responseHandled = true;
ExceptionCallback?.Invoke(fhex);
}
else
{
throw fhex;
}

return null;
}

await HttpOverride.OnNonOverridedResponseAsync(HttpMethod.Get, url, null, response, cancellationToken).ConfigureAwait(false);
}

return response;
}
}
}

Expand Down
12 changes: 12 additions & 0 deletions tests/TestWebApp.Tests/JsonClientTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,18 @@ public async Task CancellationTokenParameter()
}
}

[Fact(Timeout = Constants.TEST_TIMEOUT)]
public async Task FromServices()
{
using (var endpoint = new JsonServerInfo())
{
var client = endpoint.Provider.GetService<IValuesClient>();

await client.TestFromServicesAttributeAsync(cancellationToken: endpoint.TimeoutToken);

}
}

/// <summary>
/// Microsoft.AspNetCore.TestHost.ClientHandler does not respect the CancellationToken and will always complete a request. Their unit test around it ClientCancellationAbortsRequest has a "hack" that cancels in TestServer when the token is canceled.
/// When the HttpClient has the default HttpMessageHandler, the SendAsync will cancel approriately, until they match this functionality, this test will be disabled
Expand Down
7 changes: 7 additions & 0 deletions tests/TestWebApp/Controllers/ValuesController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using System.Threading;
using System.Threading.Tasks;
using TestWebApp.Contracts;
using TestWebApp.FakeServices;
using TestWebApp.GoodServices;

namespace TestWebApp.Controllers
Expand Down Expand Up @@ -594,5 +595,11 @@ public async Task<IActionResult> CancellationTokenApi(CancellationToken token =
return Ok();
}

[HttpGet("[action]")]
public Task TestFromServicesAttributeAsync([FromServices] IFakeService fakeService)
{
return Task.CompletedTask;
}

}
}