feat(aoc): add RequireAocGuard route helper and associated tests
- Introduced RequireAocGuard extension method for RouteHandlerBuilder to enforce AOC guard on routes. - Implemented two overloads of RequireAocGuard to support different payload selection strategies. - Added unit tests for RequireAocGuard to ensure correct behavior and exception handling. - Updated TASKS.md to reflect the addition of RequireAocGuard and related documentation. - Made internal members of Concelier.WebService visible to its test project.
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
|
||||
namespace StellaOps.Aoc.AspNetCore.Routing;
|
||||
|
||||
public static class AocGuardEndpointFilterExtensions
|
||||
{
|
||||
public static RouteHandlerBuilder RequireAocGuard<TRequest>(
|
||||
this RouteHandlerBuilder builder,
|
||||
Func<TRequest, IEnumerable<object?>> payloadSelector,
|
||||
JsonSerializerOptions? serializerOptions = null,
|
||||
AocGuardOptions? guardOptions = null)
|
||||
{
|
||||
if (builder is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
if (payloadSelector is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(payloadSelector));
|
||||
}
|
||||
|
||||
builder.Add(endpointBuilder =>
|
||||
{
|
||||
endpointBuilder.FilterFactories.Add((routeContext, next) =>
|
||||
{
|
||||
var filter = new AocGuardEndpointFilter<TRequest>(payloadSelector, serializerOptions, guardOptions);
|
||||
return invocationContext => filter.InvokeAsync(invocationContext, next);
|
||||
});
|
||||
});
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static RouteHandlerBuilder RequireAocGuard<TRequest>(
|
||||
this RouteHandlerBuilder builder,
|
||||
Func<TRequest, object?> payloadSelector,
|
||||
JsonSerializerOptions? serializerOptions = null,
|
||||
AocGuardOptions? guardOptions = null)
|
||||
{
|
||||
if (payloadSelector is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(payloadSelector));
|
||||
}
|
||||
|
||||
return AocGuardEndpointFilterExtensions.RequireAocGuard<TRequest>(
|
||||
builder,
|
||||
request =>
|
||||
{
|
||||
var payload = payloadSelector(request);
|
||||
return payload is null
|
||||
? Array.Empty<object?>()
|
||||
: new object?[] { payload };
|
||||
},
|
||||
serializerOptions,
|
||||
guardOptions);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using StellaOps.Aoc.AspNetCore.Routing;
|
||||
|
||||
namespace StellaOps.Aoc.AspNetCore.Tests;
|
||||
|
||||
public sealed class AocGuardEndpointFilterExtensionsTests
|
||||
{
|
||||
[Fact]
|
||||
public void RequireAocGuard_ReturnsBuilderInstance()
|
||||
{
|
||||
var builder = WebApplication.CreateBuilder();
|
||||
builder.Services.AddAocGuard();
|
||||
using var app = builder.Build();
|
||||
|
||||
var route = app.MapPost("/guard", (GuardPayload _) => TypedResults.Ok());
|
||||
|
||||
var result = route.RequireAocGuard<GuardPayload>(_ => Array.Empty<object?>());
|
||||
|
||||
Assert.Same(route, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RequireAocGuard_WithNullBuilder_Throws()
|
||||
{
|
||||
RouteHandlerBuilder? builder = null;
|
||||
|
||||
Assert.Throws<ArgumentNullException>(() =>
|
||||
AocGuardEndpointFilterExtensions.RequireAocGuard<GuardPayload>(
|
||||
builder!,
|
||||
_ => Array.Empty<object?>()));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RequireAocGuard_WithObjectSelector_UsesOverload()
|
||||
{
|
||||
var builder = WebApplication.CreateBuilder();
|
||||
builder.Services.AddAocGuard();
|
||||
using var app = builder.Build();
|
||||
|
||||
var route = app.MapPost("/guard-object", (GuardPayload _) => TypedResults.Ok());
|
||||
|
||||
var result = route.RequireAocGuard<GuardPayload>(_ => new GuardPayload(JsonDocument.Parse("{}").RootElement));
|
||||
|
||||
Assert.Same(route, result);
|
||||
}
|
||||
|
||||
private sealed record GuardPayload(JsonElement Payload);
|
||||
}
|
||||
Reference in New Issue
Block a user