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

Feature/nullable query params #53

Merged
merged 19 commits into from
Mar 28, 2022
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,10 @@ 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)))

serweck marked this conversation as resolved.
Show resolved Hide resolved
if (instance != null && !type.IsPrimitiveType())
{
if (!IgnoreBind(parameters[i]))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,31 +16,29 @@ public void AddTokens<TController>(TestServerAction action, TestServerTokenColle

for (var i = 0; i < parameters.Length; i++)
{
if (!IgnoreHeader(parameters[i]))
if (parameters[i].ParameterType.IsPrimitiveType()
&& !IgnoreHeader(parameters[i]))
{
if (IsPrimitiveType(parameters[i].ParameterType))
{
var tokenName = parameters[i].Name.ToLowerInvariant();
var tokenValue = action.ArgumentValues[i].Instance;
var tokenName = parameters[i].Name.ToLowerInvariant();
serweck marked this conversation as resolved.
Show resolved Hide resolved
var tokenValue = action.ArgumentValues.Any(x => x.Key == i) ? action.ArgumentValues[i].Instance : null;

if (tokenValue != null)
{
tokens.AddToken(tokenName, tokenValue.ToString(), isConventional: false);
}
}
else if (parameters[i].ParameterType.IsArray
&& IsPrimitiveType(parameters[i].ParameterType.GetElementType()))
if (tokenValue != null)
{
var arrayValues = (Array)action.ArgumentValues[i].Instance;
tokens.AddToken(tokenName, tokenValue.ToString(), isConventional: false);
}
}
else if (parameters[i].ParameterType.IsArray
serweck marked this conversation as resolved.
Show resolved Hide resolved
&& IsPrimitiveType(parameters[i].ParameterType.GetElementType()))
{
var arrayValues = (Array)action.ArgumentValues[i].Instance;

if (arrayValues != null
&& arrayValues.Length != 0
)
{
var tokenName = parameters[i].Name.ToLowerInvariant();
var tokenValue = GetTokenValue(arrayValues, tokenName);
tokens.AddToken(tokenName, tokenValue, isConventional: false);
}
if (arrayValues != null
&& arrayValues.Length != 0
)
{
var tokenName = parameters[i].Name.ToLowerInvariant();
var tokenValue = GetTokenValue(arrayValues, tokenName);
tokens.AddToken(tokenName, tokenValue, isConventional: false);
}
}
}
Expand Down
17 changes: 17 additions & 0 deletions src/Acheve.TestHost/Routing/Tokenizers/PrimitiveTypeHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System;

namespace Acheve.TestHost.Routing.Tokenizers
{
public static class PrimitiveTypeHelper
serweck marked this conversation as resolved.
Show resolved Hide resolved
serweck marked this conversation as resolved.
Show resolved Hide resolved
{
public 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