Skip to content
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
78 changes: 78 additions & 0 deletions src/Http/Wolverine.Http.Tests/using_querystring_parameters.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
using JasperFx.CodeGeneration.Frames;
using Shouldly;
using Wolverine.Http.CodeGen;
using Wolverine.Runtime;
using WolverineWebApi;

namespace Wolverine.Http.Tests;

Expand Down Expand Up @@ -198,6 +202,68 @@ public async Task use_parsed_enum_collection()
body.ReadAsText().ShouldBe($"North,East,South");
}

[Fact]
public async Task using_string_array_completely_hit()
{
var body = await Scenario(x =>
{
x.Get
.Url("/querystring/stringarray")
.QueryString("values", "foo")
.QueryString("values", "bar")
.QueryString("values", "baz");

x.Header("content-type").SingleValueShouldEqual("text/plain");
});

body.ReadAsText().ShouldBe("foo,bar,baz");
}

[Fact]
public async Task using_string_array_completely_miss()
{
var body = await Scenario(x =>
{
x.Get
.Url("/querystring/stringarray");

x.Header("content-type").SingleValueShouldEqual("text/plain");
});

body.ReadAsText().ShouldBe("none");
}

[Fact]
public async Task using_int_array_completely_hit()
{
var body = await Scenario(x =>
{
x.Get
.Url("/querystring/intarray")
.QueryString("values", "4")
.QueryString("values", "2")
.QueryString("values", "1");

x.Header("content-type").SingleValueShouldEqual("text/plain");
});

body.ReadAsText().ShouldBe("1,2,4");
}

[Fact]
public async Task using_int_array_completely_miss()
{
var body = await Scenario(x =>
{
x.Get
.Url("/querystring/intarray");

x.Header("content-type").SingleValueShouldEqual("text/plain");
});

body.ReadAsText().ShouldBe("none");
}

#region sample_query_string_usage

[Fact]
Expand Down Expand Up @@ -238,4 +304,16 @@ public async Task use_decimal_querystring_hit()
}

#endregion

[Fact]
public void trouble_shoot_querystring_matching()
{
var method = new MethodCall(typeof(QuerystringEndpoints), "IntArray");
var chain = new HttpChain(method, new HttpGraph(new WolverineOptions(), ServiceContainer.Empty()));

var parameter = method.Method.GetParameters().Single();

var variable = chain.TryFindOrCreateQuerystringValue(parameter);
variable.Creator.ShouldBeOfType<ParsedArrayQueryStringValue>();
}
}
56 changes: 56 additions & 0 deletions src/Http/Wolverine.Http/CodeGen/ParsedArrayQueryStringValue.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using System.Reflection;
using JasperFx.CodeGeneration;
using JasperFx.CodeGeneration.Frames;
using JasperFx.Core.Reflection;

namespace Wolverine.Http.CodeGen;

internal class ParsedArrayQueryStringValue : SyncFrame
{
public ParsedArrayQueryStringValue(ParameterInfo parameter)
{
Variable = new QuerystringVariable(parameter.ParameterType, parameter.Name!, this);
}

public QuerystringVariable Variable { get; }

public override void GenerateCode(GeneratedMethod method, ISourceWriter writer)
{
var elementType = Variable.VariableType.GetElementType();
if (elementType == typeof(string))
{
writer.Write($"var {Variable.Usage} = httpContext.Request.Query[\"{Variable.Usage}\"].ToArray();");
}
else
{
var collectionAlias = typeof(List<>).MakeGenericType(elementType).FullNameInCode();
var elementAlias = elementType.FullNameInCode();

writer.Write($"var {Variable.Usage}_List = new {collectionAlias}();");

writer.Write($"BLOCK:foreach (var {Variable.Usage}Value in httpContext.Request.Query[\"{Variable.Usage}\"])");

if (elementType.IsEnum)
{
writer.Write($"BLOCK:if ({elementAlias}.TryParse<{elementAlias}>({Variable.Usage}Value, out var {Variable.Usage}ValueParsed))");
}
else if (elementType.IsBoolean())
{
writer.Write($"BLOCK:if ({elementAlias}.TryParse({Variable.Usage}Value, out var {Variable.Usage}ValueParsed))");
}
else
{
writer.Write($"BLOCK:if ({elementAlias}.TryParse({Variable.Usage}Value, System.Globalization.CultureInfo.InvariantCulture, out var {Variable.Usage}ValueParsed))");
}

writer.Write($"{Variable.Usage}_List.Add({Variable.Usage}ValueParsed);");
writer.FinishBlock(); // parsing block

writer.FinishBlock(); // foreach blobck

writer.Write($"var {Variable.Usage} = {Variable.Usage}_List.ToArray();");
}

Next?.GenerateCode(method, writer);
}
}
16 changes: 16 additions & 0 deletions src/Http/Wolverine.Http/HttpChain.cs
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,14 @@ private void applyMetadata()
if (parameter.ParameterType == typeof(string))
{
variable = new ReadStringQueryStringValue(key).Variable;
variable.Name = key;
_querystringVariables.Add(variable);
}

if (parameter.ParameterType == typeof(string[]))
{
variable = new ParsedArrayQueryStringValue(parameter).Variable;
variable.Name = key;
_querystringVariables.Add(variable);
}

Expand All @@ -330,10 +338,18 @@ private void applyMetadata()
_querystringVariables.Add(variable);
}
}

if (parameter.ParameterType.IsArray && RouteParameterStrategy.CanParse(parameter.ParameterType.GetElementType()))
{
variable = new ParsedArrayQueryStringValue(parameter).Variable;
variable.Name = key;
_querystringVariables.Add(variable);
}

if (ParsedCollectionQueryStringValue.CanParse(parameter.ParameterType))
{
variable = new ParsedCollectionQueryStringValue(parameter).Variable;
variable.Name = key;
_querystringVariables.Add(variable);
}

Expand Down
44 changes: 44 additions & 0 deletions src/Http/WolverineWebApi/QuerystringEndpoints.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using JasperFx.Core;
using Marten;
using Microsoft.AspNetCore.Mvc;
using Wolverine.Http;

namespace WolverineWebApi;

public static class QuerystringEndpoints
{

[WolverineGet("/querystring/enum")]
public static string UsingEnumQuerystring(Direction direction)
{
return direction.ToString();
}

[WolverineGet("/querystring/explicit")]
public static string UsingEnumQuerystring([FromQuery(Name = "name")]string value)
{
return value ?? "";
}

[WolverineGet("/querystring/enum/nullable")]
public static string UsingNullableEnumQuerystring(Direction? direction)
{
return direction?.ToString() ?? "none";
}

[WolverineGet("/querystring/stringarray")]
public static string StringArray(string[]? values)
{
if (values == null || values.IsEmpty()) return "none";

return values.Join(",");
}

[WolverineGet("/querystring/intarray")]
public static string IntArray(int[]? values)
{
if (values == null || values.IsEmpty()) return "none";

return values.OrderBy(x => x).Select(x => x.ToString()).Join(",");
}
}
26 changes: 3 additions & 23 deletions src/Http/WolverineWebApi/TestEndpoints.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System.Globalization;
using Marten;
using Microsoft.AspNetCore.Mvc;
using Wolverine.Http;
using JasperFx.Core;

namespace WolverineWebApi;

Expand Down Expand Up @@ -136,28 +136,8 @@ public static string UsingEnumCollection(IEnumerable<Direction> collection)
{
return string.Join(",", collection);
}
}

public static class QuerystringEndpoints
{

[WolverineGet("/querystring/enum")]
public static string UsingEnumQuerystring(Direction direction)
{
return direction.ToString();
}

[WolverineGet("/querystring/explicit")]
public static string UsingEnumQuerystring([FromQuery(Name = "name")]string value)
{
return value ?? "";
}

[WolverineGet("/querystring/enum/nullable")]
public static string UsingNullableEnumQuerystring(Direction? direction)
{
return direction?.ToString() ?? "none";
}


}

public class ArithmeticResults
Expand Down
Loading