Skip to content

Commit 23a25df

Browse files
Allow DateOnly to be injected and returned
1 parent f46397b commit 23a25df

File tree

6 files changed

+140
-8
lines changed

6 files changed

+140
-8
lines changed

Modules/Controllers/Extensions.cs

+30-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1-
using GenHTTP.Modules.Layouting.Provider;
1+
using System.Diagnostics.CodeAnalysis;
22

3-
using System.Diagnostics.CodeAnalysis;
3+
using GenHTTP.Api.Infrastructure;
4+
5+
using GenHTTP.Modules.Controllers.Provider;
6+
using GenHTTP.Modules.Conversion.Providers;
7+
using GenHTTP.Modules.Layouting.Provider;
8+
using GenHTTP.Modules.Reflection.Injectors;
49

510
namespace GenHTTP.Modules.Controllers
611
{
@@ -15,9 +20,11 @@ public static class Extensions
1520
/// <typeparam name="T">The type of the controller used to handle requests</typeparam>
1621
/// <param name="builder">The layout the controller should be added to</param>
1722
/// <param name="path">The path that should be handled by the controller</param>
18-
public static LayoutBuilder AddController<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(this LayoutBuilder builder, string path) where T : new()
23+
/// <param name="injectors">Optionally the injectors to be used by this controller</param>
24+
/// <param name="formats">Optionally the formats to be used by this controller</param>
25+
public static LayoutBuilder AddController<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(this LayoutBuilder builder, string path, IBuilder<InjectionRegistry>? injectors = null, IBuilder<SerializationRegistry>? formats = null) where T : new()
1926
{
20-
builder.Add(path, Controller.From<T>());
27+
builder.Add(path, Controller.From<T>().Configured(injectors, formats));
2128
return builder;
2229
}
2330

@@ -27,9 +34,26 @@ public static class Extensions
2734
/// </summary>
2835
/// <typeparam name="T">The type of the controller used to handle requests</typeparam>
2936
/// <param name="builder">The layout the controller should be added to</param>
30-
public static LayoutBuilder IndexController<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(this LayoutBuilder builder) where T : new()
37+
/// <param name="injectors">Optionally the injectors to be used by this controller</param>
38+
/// <param name="formats">Optionally the formats to be used by this controller</param>
39+
public static LayoutBuilder IndexController<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(this LayoutBuilder builder, IBuilder<InjectionRegistry>? injectors = null, IBuilder<SerializationRegistry>? formats = null) where T : new()
3140
{
32-
builder.Add(Controller.From<T>());
41+
builder.Add(Controller.From<T>().Configured(injectors, formats));
42+
return builder;
43+
}
44+
45+
private static ControllerBuilder<T> Configured<T>(this ControllerBuilder<T> builder, IBuilder<InjectionRegistry>? injectors = null, IBuilder<SerializationRegistry>? formats = null) where T : new()
46+
{
47+
if (injectors != null)
48+
{
49+
builder.Injectors(injectors);
50+
}
51+
52+
if (formats != null)
53+
{
54+
builder.Formats(formats);
55+
}
56+
3357
return builder;
3458
}
3559

Modules/Controllers/Provider/ControllerHandler.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public ControllerHandler(IHandler parent, SerializationRegistry formats, Injecti
3737
{
3838
Parent = parent;
3939

40-
ResponseProvider = new(null);
40+
ResponseProvider = new(formats);
4141

4242
Provider = new(this, AnalyzeMethods(typeof(T), formats, injection));
4343
}

Modules/Conversion/Extensions.cs

+24
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Globalization;
3+
using System.Text.RegularExpressions;
34

45
using GenHTTP.Api.Content;
56
using GenHTTP.Api.Protocol;
@@ -10,6 +11,8 @@ namespace GenHTTP.Modules.Conversion
1011
public static class Extensions
1112
{
1213

14+
private static readonly Regex DATE_ONLY_PATTERN = new Regex(@"^([0-9]{4})\-([0-9]{2})\-([0-9]{2})$");
15+
1316
/// <summary>
1417
/// Attempts to convert the given string value to the specified type.
1518
/// </summary>
@@ -54,6 +57,11 @@ public static class Extensions
5457
return Guid.Parse(value);
5558
}
5659

60+
if (actualType == typeof(DateOnly))
61+
{
62+
return ParseDateOnly(value);
63+
}
64+
5765
return Convert.ChangeType(value, actualType, CultureInfo.InvariantCulture);
5866
}
5967
catch (Exception e)
@@ -62,6 +70,22 @@ public static class Extensions
6270
}
6371
}
6472

73+
private static DateOnly ParseDateOnly(string input)
74+
{
75+
var match = DATE_ONLY_PATTERN.Match(input);
76+
77+
if (match.Success)
78+
{
79+
var year = int.Parse(match.Groups[1].Value);
80+
var month = int.Parse(match.Groups[2].Value);
81+
var day = int.Parse(match.Groups[3].Value);
82+
83+
return new DateOnly(year, month, day);
84+
}
85+
86+
throw new ArgumentException($"Input does not match the requested format (yyyy-mm-dd): {input}");
87+
}
88+
6589
}
6690

6791
}

Modules/Reflection/Extensions.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ public static class Extensions
1414
/// <returns><c>true</c>, if the given parameter can be passed via the URL</returns>
1515
public static bool CheckSimple(this ParameterInfo info)
1616
{
17-
return info.CheckNullable() || info.ParameterType.IsPrimitive || info.ParameterType == typeof(string) || info.ParameterType.IsEnum || info.ParameterType == typeof(Guid);
17+
return info.CheckNullable() || info.ParameterType.IsPrimitive || info.ParameterType == typeof(string) || info.ParameterType.IsEnum
18+
|| info.ParameterType == typeof(Guid) || info.ParameterType == typeof(DateOnly);
1819
}
1920

2021
/// <summary>

Modules/Reflection/ResponseProvider.cs

+11
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,17 @@ public ResponseProvider(SerializationRegistry? serialization)
9797
if (Serialization is not null)
9898
{
9999
// basic types should produce a string value
100+
if (type == typeof(DateOnly))
101+
{
102+
var date = (DateOnly)result;
103+
104+
return request.Respond()
105+
.Content(date.ToString("yyyy-MM-dd"))
106+
.Type(ContentType.TextPlain)
107+
.Adjust(adjustments)
108+
.Build();
109+
}
110+
100111
if (type.IsPrimitive || type == typeof(string) || type.IsEnum || type == typeof(Guid))
101112
{
102113
return request.Respond()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Net;
4+
using System.Net.Http;
5+
using System.Threading.Tasks;
6+
7+
using GenHTTP.Api.Protocol;
8+
using GenHTTP.Modules.Controllers;
9+
using GenHTTP.Modules.Conversion;
10+
using GenHTTP.Modules.Layouting;
11+
12+
using Microsoft.VisualStudio.TestTools.UnitTesting;
13+
14+
namespace GenHTTP.Testing.Acceptance.Modules.Controllers
15+
{
16+
17+
[TestClass]
18+
public sealed class DataTests
19+
{
20+
21+
#region Controller
22+
23+
public class TestController
24+
{
25+
26+
[ControllerAction(RequestMethod.POST)]
27+
public DateOnly Date(DateOnly date) => date;
28+
29+
}
30+
31+
#endregion
32+
33+
#region Tests
34+
35+
[TestMethod]
36+
public async Task TestDateOnly()
37+
{
38+
using var host = GetHost();
39+
40+
var request = host.GetRequest("/t/date/", HttpMethod.Post);
41+
42+
var data = new Dictionary<string, string>()
43+
{
44+
{ "date", "2024-03-11" }
45+
};
46+
47+
request.Content = new FormUrlEncodedContent(data);
48+
49+
using var response = await host.GetResponseAsync(request);
50+
51+
await response.AssertStatusAsync(HttpStatusCode.OK);
52+
53+
Assert.AreEqual("2024-03-11", await response.GetContentAsync());
54+
}
55+
56+
#endregion
57+
58+
#region Helpers
59+
60+
private static TestHost GetHost()
61+
{
62+
var app = Layout.Create()
63+
.AddController<TestController>("t", formats: Serialization.Default());
64+
65+
return TestHost.Run(app);
66+
}
67+
68+
#endregion
69+
70+
}
71+
72+
}

0 commit comments

Comments
 (0)