Skip to content

Commit

Permalink
Feature/nullable query params (#53)
Browse files Browse the repository at this point in the history
* Add Test For try fix a Bug

* Allo Query Parameter Nullable

* allow .net 3.1 build

* add folder again

* fix indent

* fix indent

* indent

* indent

* fix merge

* Change class name and access

* Pr Comments

* better comment method

* comment

* improve performance

Co-authored-by: Alexis Martin Peña <[email protected]>
  • Loading branch information
serweck and Alexis Martin Peña authored Mar 28, 2022
1 parent 9d6a353 commit 9c82519
Show file tree
Hide file tree
Showing 8 changed files with 102 additions and 27 deletions.
6 changes: 6 additions & 0 deletions src/Acheve.TestHost/HttpResponseMessageExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ public static async Task IsSuccessStatusCodeOrThrow(this HttpResponseMessage res
throw new Exception($"Response status does not indicate success: {response.StatusCode:D} ({response.StatusCode}); \r\n{content}");
}

/// <summary>
/// Read HttpResponseMessage and convert to T Class
/// </summary>
/// <typeparam name="T">Class</typeparam>
/// <param name="responseMessage">The httpResponseMessage instance</param>
/// <returns>T class object</returns>
public static async Task<T> ReadContentAsAsync<T>(this HttpResponseMessage responseMessage)
{
var json = await responseMessage.Content.ReadAsStringAsync();
Expand Down
56 changes: 35 additions & 21 deletions src/Acheve.TestHost/Routing/TestServerAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,34 +36,48 @@ public void AddArgument(int order, Expression expression, bool activeBodyApiCont

if (!ArgumentValues.ContainsKey(order))
{
switch (expression)
if (IsNullable(argument.ParameterType))
{
case ConstantExpression constant:
{
ArgumentValues.Add(order, new TestServerArgument(constant.Value?.ToString(), isFromBody, isFromForm, isFromHeader, argument.Name));
}
break;
var expressionValue = Expression.Lambda(expression).Compile().DynamicInvoke();

case MemberExpression member when member.NodeType == ExpressionType.MemberAccess:
{
var instance = Expression.Lambda(member)
.Compile()
.DynamicInvoke();
if (expressionValue != null)
{
ArgumentValues.Add(order, new TestServerArgument(expressionValue.ToString(), isFromBody, isFromForm, isFromHeader, argument.Name));
}
}
else
{
switch (expression)
{
case ConstantExpression constant:
{
ArgumentValues.Add(order, new TestServerArgument(constant.Value?.ToString(), isFromBody, isFromForm, isFromHeader, argument.Name));
}
break;

ArgumentValues.Add(order, new TestServerArgument(instance, isFromBody, isFromForm, isFromHeader, argument.Name));
}
break;
case MemberExpression member when member.NodeType == ExpressionType.MemberAccess:
{
var instance = Expression.Lambda(member)
.Compile()
.DynamicInvoke();

case MethodCallExpression method:
{
var instance = Expression.Lambda(method).Compile().DynamicInvoke();
ArgumentValues.Add(order, new TestServerArgument(instance, isFromBody, isFromForm, isFromHeader, argument.Name));
}
break;
ArgumentValues.Add(order, new TestServerArgument(instance, isFromBody, isFromForm, isFromHeader, argument.Name));
}
break;

default: return;
case MethodCallExpression method:
{
var instance = Expression.Lambda(method).Compile().DynamicInvoke();
ArgumentValues.Add(order, new TestServerArgument(instance, isFromBody, isFromForm, isFromHeader, argument.Name));
}
break;

default: return;
}
}
}
}

bool IsNullable(Type type) => Nullable.GetUnderlyingType(type) != null;
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using System;
using System.Linq;
using System.Reflection;

namespace Acheve.TestHost.Routing.Tokenizers
Expand All @@ -15,9 +16,9 @@ public void AddTokens<TController>(TestServerAction action, TestServerTokenColle
for (int i = 0; i < parameters.Length; i++)
{
var type = parameters[i].ParameterType;
var instance = action.ArgumentValues[i].Instance;
var instance = action.ArgumentValues.Any(x => x.Key == i) ? action.ArgumentValues[i].Instance : null;

if (instance != null && !(type.IsPrimitive || type == typeof(String) || type == typeof(Decimal) || type == typeof(Guid)))
if (instance != null && !type.IsPrimitiveType())
{
if (!IgnoreBind(parameters[i]))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@ public void AddTokens<TController>(TestServerAction action, TestServerTokenColle
{
if (!IgnoreHeader(parameters[i]))
{
if (IsPrimitiveType(parameters[i].ParameterType))
var tokenName = parameters[i].Name.ToLowerInvariant();

if (parameters[i].ParameterType.IsPrimitiveType())
{
var tokenName = parameters[i].Name.ToLowerInvariant();
var tokenValue = action.ArgumentValues[i].Instance;
var tokenValue = action.ArgumentValues.Any(x => x.Key == i) ? action.ArgumentValues[i].Instance : null;

if (tokenValue != null)
{
Expand All @@ -37,7 +38,6 @@ public void AddTokens<TController>(TestServerAction action, TestServerTokenColle
&& arrayValues.Length != 0
)
{
var tokenName = parameters[i].Name.ToLowerInvariant();
var tokenValue = GetTokenValue(arrayValues, tokenName);
tokens.AddToken(tokenName, tokenValue, isConventional: false);
}
Expand Down
15 changes: 15 additions & 0 deletions src/Acheve.TestHost/Routing/Tokenizers/TypeExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace System
{
internal static class TypeExtensions
{
internal static bool IsPrimitiveType(this Type typeToInspect)
{
var type = Nullable.GetUnderlyingType(typeToInspect) ?? typeToInspect;

return type.IsPrimitive
|| type == typeof(string)
|| type == typeof(decimal)
|| type == typeof(Guid);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ public IActionResult UnderDashSupport(Guid param_1, int param_2)
return Ok();
}

[HttpGet("nullableQueryParams")]
public ActionResult<NullableQueryParamsResponse> NullableQueryParams(bool? param1, Guid? param2)
{
return Ok(new NullableQueryParamsResponse { Param1 = param1, Param2 = param2 });
}

[HttpGet("arrayGuid")]
public ActionResult<Guid[]> GuidArraySupport([FromQuery] Guid[] param1)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System;

namespace UnitTests.Acheve.TestHost.Routing.Models
{
public class NullableQueryParamsResponse
{
public bool? Param1 { get; set; }
public Guid? Param2 { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1346,6 +1346,29 @@ public void create_valid_request_supporting_underdash_on_router_params()
.Should().Be($"api/bugs/{guid}/10");
}

[Fact]
public async Task create_valid_request_supporting_nullable_params_on_query()
{
var server = new TestServerBuilder()
.UseDefaultStartup()
.Build();

var guid = Guid.NewGuid();

var request = server.CreateHttpApiRequest<BugsController>(
actionSelector: controller => controller.NullableQueryParams(null, guid),
tokenValues: null,
contentOptions: new NotIncludeContent());

var responseMessage = await request.GetAsync();

responseMessage.EnsureSuccessStatusCode();
var response = await responseMessage.ReadContentAsAsync<NullableQueryParamsResponse>();

response.Param1.Should().Be(null);
response.Param2.Should().Be(guid);
}

[Fact]
public async Task create_request_supporting_guid_array_types_on_parameters()
{
Expand Down

0 comments on commit 9c82519

Please sign in to comment.