Skip to content

Commit 9721c36

Browse files
committed
ability to use array types as query string values in Wolverine.HTTP. Closes GH-1137
1 parent 374293d commit 9721c36

File tree

5 files changed

+197
-23
lines changed

5 files changed

+197
-23
lines changed

src/Http/Wolverine.Http.Tests/using_querystring_parameters.cs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1+
using JasperFx.CodeGeneration.Frames;
12
using Shouldly;
3+
using Wolverine.Http.CodeGen;
4+
using Wolverine.Runtime;
5+
using WolverineWebApi;
26

37
namespace Wolverine.Http.Tests;
48

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

205+
[Fact]
206+
public async Task using_string_array_completely_hit()
207+
{
208+
var body = await Scenario(x =>
209+
{
210+
x.Get
211+
.Url("/querystring/stringarray")
212+
.QueryString("values", "foo")
213+
.QueryString("values", "bar")
214+
.QueryString("values", "baz");
215+
216+
x.Header("content-type").SingleValueShouldEqual("text/plain");
217+
});
218+
219+
body.ReadAsText().ShouldBe("foo,bar,baz");
220+
}
221+
222+
[Fact]
223+
public async Task using_string_array_completely_miss()
224+
{
225+
var body = await Scenario(x =>
226+
{
227+
x.Get
228+
.Url("/querystring/stringarray");
229+
230+
x.Header("content-type").SingleValueShouldEqual("text/plain");
231+
});
232+
233+
body.ReadAsText().ShouldBe("none");
234+
}
235+
236+
[Fact]
237+
public async Task using_int_array_completely_hit()
238+
{
239+
var body = await Scenario(x =>
240+
{
241+
x.Get
242+
.Url("/querystring/intarray")
243+
.QueryString("values", "4")
244+
.QueryString("values", "2")
245+
.QueryString("values", "1");
246+
247+
x.Header("content-type").SingleValueShouldEqual("text/plain");
248+
});
249+
250+
body.ReadAsText().ShouldBe("1,2,4");
251+
}
252+
253+
[Fact]
254+
public async Task using_int_array_completely_miss()
255+
{
256+
var body = await Scenario(x =>
257+
{
258+
x.Get
259+
.Url("/querystring/intarray");
260+
261+
x.Header("content-type").SingleValueShouldEqual("text/plain");
262+
});
263+
264+
body.ReadAsText().ShouldBe("none");
265+
}
266+
201267
#region sample_query_string_usage
202268

203269
[Fact]
@@ -238,4 +304,16 @@ public async Task use_decimal_querystring_hit()
238304
}
239305

240306
#endregion
307+
308+
[Fact]
309+
public void trouble_shoot_querystring_matching()
310+
{
311+
var method = new MethodCall(typeof(QuerystringEndpoints), "IntArray");
312+
var chain = new HttpChain(method, new HttpGraph(new WolverineOptions(), ServiceContainer.Empty()));
313+
314+
var parameter = method.Method.GetParameters().Single();
315+
316+
var variable = chain.TryFindOrCreateQuerystringValue(parameter);
317+
variable.Creator.ShouldBeOfType<ParsedArrayQueryStringValue>();
318+
}
241319
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
using System.Reflection;
2+
using JasperFx.CodeGeneration;
3+
using JasperFx.CodeGeneration.Frames;
4+
using JasperFx.Core.Reflection;
5+
6+
namespace Wolverine.Http.CodeGen;
7+
8+
internal class ParsedArrayQueryStringValue : SyncFrame
9+
{
10+
public ParsedArrayQueryStringValue(ParameterInfo parameter)
11+
{
12+
Variable = new QuerystringVariable(parameter.ParameterType, parameter.Name!, this);
13+
}
14+
15+
public QuerystringVariable Variable { get; }
16+
17+
public override void GenerateCode(GeneratedMethod method, ISourceWriter writer)
18+
{
19+
var elementType = Variable.VariableType.GetElementType();
20+
if (elementType == typeof(string))
21+
{
22+
writer.Write($"var {Variable.Usage} = httpContext.Request.Query[\"{Variable.Usage}\"].ToArray();");
23+
}
24+
else
25+
{
26+
var collectionAlias = typeof(List<>).MakeGenericType(elementType).FullNameInCode();
27+
var elementAlias = elementType.FullNameInCode();
28+
29+
writer.Write($"var {Variable.Usage}_List = new {collectionAlias}();");
30+
31+
writer.Write($"BLOCK:foreach (var {Variable.Usage}Value in httpContext.Request.Query[\"{Variable.Usage}\"])");
32+
33+
if (elementType.IsEnum)
34+
{
35+
writer.Write($"BLOCK:if ({elementAlias}.TryParse<{elementAlias}>({Variable.Usage}Value, out var {Variable.Usage}ValueParsed))");
36+
}
37+
else if (elementType.IsBoolean())
38+
{
39+
writer.Write($"BLOCK:if ({elementAlias}.TryParse({Variable.Usage}Value, out var {Variable.Usage}ValueParsed))");
40+
}
41+
else
42+
{
43+
writer.Write($"BLOCK:if ({elementAlias}.TryParse({Variable.Usage}Value, System.Globalization.CultureInfo.InvariantCulture, out var {Variable.Usage}ValueParsed))");
44+
}
45+
46+
writer.Write($"{Variable.Usage}_List.Add({Variable.Usage}ValueParsed);");
47+
writer.FinishBlock(); // parsing block
48+
49+
writer.FinishBlock(); // foreach blobck
50+
51+
writer.Write($"var {Variable.Usage} = {Variable.Usage}_List.ToArray();");
52+
}
53+
54+
Next?.GenerateCode(method, writer);
55+
}
56+
}

src/Http/Wolverine.Http/HttpChain.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,14 @@ private void applyMetadata()
317317
if (parameter.ParameterType == typeof(string))
318318
{
319319
variable = new ReadStringQueryStringValue(key).Variable;
320+
variable.Name = key;
321+
_querystringVariables.Add(variable);
322+
}
323+
324+
if (parameter.ParameterType == typeof(string[]))
325+
{
326+
variable = new ParsedArrayQueryStringValue(parameter).Variable;
327+
variable.Name = key;
320328
_querystringVariables.Add(variable);
321329
}
322330

@@ -330,10 +338,18 @@ private void applyMetadata()
330338
_querystringVariables.Add(variable);
331339
}
332340
}
341+
342+
if (parameter.ParameterType.IsArray && RouteParameterStrategy.CanParse(parameter.ParameterType.GetElementType()))
343+
{
344+
variable = new ParsedArrayQueryStringValue(parameter).Variable;
345+
variable.Name = key;
346+
_querystringVariables.Add(variable);
347+
}
333348

334349
if (ParsedCollectionQueryStringValue.CanParse(parameter.ParameterType))
335350
{
336351
variable = new ParsedCollectionQueryStringValue(parameter).Variable;
352+
variable.Name = key;
337353
_querystringVariables.Add(variable);
338354
}
339355

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using JasperFx.Core;
2+
using Marten;
3+
using Microsoft.AspNetCore.Mvc;
4+
using Wolverine.Http;
5+
6+
namespace WolverineWebApi;
7+
8+
public static class QuerystringEndpoints
9+
{
10+
11+
[WolverineGet("/querystring/enum")]
12+
public static string UsingEnumQuerystring(Direction direction)
13+
{
14+
return direction.ToString();
15+
}
16+
17+
[WolverineGet("/querystring/explicit")]
18+
public static string UsingEnumQuerystring([FromQuery(Name = "name")]string value)
19+
{
20+
return value ?? "";
21+
}
22+
23+
[WolverineGet("/querystring/enum/nullable")]
24+
public static string UsingNullableEnumQuerystring(Direction? direction)
25+
{
26+
return direction?.ToString() ?? "none";
27+
}
28+
29+
[WolverineGet("/querystring/stringarray")]
30+
public static string StringArray(string[]? values)
31+
{
32+
if (values == null || values.IsEmpty()) return "none";
33+
34+
return values.Join(",");
35+
}
36+
37+
[WolverineGet("/querystring/intarray")]
38+
public static string IntArray(int[]? values)
39+
{
40+
if (values == null || values.IsEmpty()) return "none";
41+
42+
return values.OrderBy(x => x).Select(x => x.ToString()).Join(",");
43+
}
44+
}

src/Http/WolverineWebApi/TestEndpoints.cs

Lines changed: 3 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
using System.Globalization;
22
using Marten;
3-
using Microsoft.AspNetCore.Mvc;
43
using Wolverine.Http;
4+
using JasperFx.Core;
55

66
namespace WolverineWebApi;
77

@@ -136,28 +136,8 @@ public static string UsingEnumCollection(IEnumerable<Direction> collection)
136136
{
137137
return string.Join(",", collection);
138138
}
139-
}
140-
141-
public static class QuerystringEndpoints
142-
{
143-
144-
[WolverineGet("/querystring/enum")]
145-
public static string UsingEnumQuerystring(Direction direction)
146-
{
147-
return direction.ToString();
148-
}
149-
150-
[WolverineGet("/querystring/explicit")]
151-
public static string UsingEnumQuerystring([FromQuery(Name = "name")]string value)
152-
{
153-
return value ?? "";
154-
}
155-
156-
[WolverineGet("/querystring/enum/nullable")]
157-
public static string UsingNullableEnumQuerystring(Direction? direction)
158-
{
159-
return direction?.ToString() ?? "none";
160-
}
139+
140+
161141
}
162142

163143
public class ArithmeticResults

0 commit comments

Comments
 (0)