Skip to content

Commit

Permalink
Merge pull request #238 from cnblogs/support-async-command-creation
Browse files Browse the repository at this point in the history
feat: support async query&command creation on minimal api mapping
  • Loading branch information
ikesnowy authored May 16, 2024
2 parents 2d39d1f + b307885 commit 1b061fe
Show file tree
Hide file tree
Showing 8 changed files with 74 additions and 28 deletions.
75 changes: 56 additions & 19 deletions src/Cnblogs.Architecture.Ddd.Cqrs.AspNetCore/CqrsRouteMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,29 @@ public static class CqrsRouteMapper

private static readonly string[] GetAndHeadMethods = { "GET", "HEAD" };

private static readonly List<string> PostCommandPrefixes = new() { "Create", "Add", "New" };
private static readonly List<string> PostCommandPrefixes = new()
{
"Create",
"Add",
"New"
};

private static readonly List<string> PutCommandPrefixes = new() { "Update", "Modify", "Replace", "Alter" };
private static readonly List<string> PutCommandPrefixes = new()
{
"Update",
"Modify",
"Replace",
"Alter"
};

private static readonly List<string> DeleteCommandPrefixes = new() { "Delete", "Remove", "Clean", "Clear", "Purge" };
private static readonly List<string> DeleteCommandPrefixes = new()
{
"Delete",
"Remove",
"Clean",
"Clear",
"Purge"
};

/// <summary>
/// Map a query API, using GET method. <typeparamref name="T"/> would been constructed from route and query string.
Expand Down Expand Up @@ -96,14 +114,7 @@ public static IEndpointConventionBuilder MapQuery(
string nullRouteParameterPattern = "-",
bool enableHead = false)
{
var isQuery = handler.Method.ReturnType.GetInterfaces().Where(x => x.IsGenericType)
.Any(x => QueryTypes.Contains(x.GetGenericTypeDefinition()));
if (isQuery == false)
{
throw new ArgumentException(
"delegate does not return a query, please make sure it returns object that implement IQuery<> or IListQuery<> or interface that inherit from them");
}

var returnType = EnsureReturnTypeIsQuery(handler);
if (mapNullableRouteParameters is MapNullableRouteParameter.Disable)
{
return MapRoutes(route);
Expand All @@ -118,7 +129,7 @@ public static IEndpointConventionBuilder MapQuery(

var parsedRoute = RoutePatternFactory.Parse(route);
var context = new NullabilityInfoContext();
var nullableRouteProperties = handler.Method.ReturnType.GetProperties()
var nullableRouteProperties = returnType.GetProperties()
.Where(
p => p.GetMethod != null
&& p.SetMethod != null
Expand Down Expand Up @@ -209,8 +220,7 @@ public static IEndpointConventionBuilder MapCommand(
[StringSyntax("Route")] string route,
Delegate handler)
{
EnsureDelegateReturnTypeIsCommand(handler);
var commandTypeName = handler.Method.ReturnType.Name;
var commandTypeName = EnsureReturnTypeIsCommand(handler).Name;
if (PostCommandPrefixes.Any(x => commandTypeName.StartsWith(x)))
{
return app.MapPostCommand(route, handler);
Expand Down Expand Up @@ -255,7 +265,7 @@ public static IEndpointConventionBuilder MapPostCommand(
[StringSyntax("Route")] string route,
Delegate handler)
{
EnsureDelegateReturnTypeIsCommand(handler);
EnsureReturnTypeIsCommand(handler);
return app.MapPost(route, handler).AddEndpointFilter<CommandEndpointHandler>();
}

Expand Down Expand Up @@ -285,7 +295,7 @@ public static IEndpointConventionBuilder MapPutCommand(
[StringSyntax("Route")] string route,
Delegate handler)
{
EnsureDelegateReturnTypeIsCommand(handler);
EnsureReturnTypeIsCommand(handler);
return app.MapPut(route, handler).AddEndpointFilter<CommandEndpointHandler>();
}

Expand Down Expand Up @@ -315,7 +325,7 @@ public static IEndpointConventionBuilder MapDeleteCommand(
[StringSyntax("Route")] string route,
Delegate handler)
{
EnsureDelegateReturnTypeIsCommand(handler);
EnsureReturnTypeIsCommand(handler);
return app.MapDelete(route, handler).AddEndpointFilter<CommandEndpointHandler>();
}

Expand Down Expand Up @@ -385,15 +395,42 @@ public static IEndpointRouteBuilder StopMappingPrefixToDelete(this IEndpointRout
return app;
}

private static void EnsureDelegateReturnTypeIsCommand(Delegate handler)
private static Type EnsureReturnTypeIsCommand(Delegate handler)
{
var isCommand = handler.Method.ReturnType.GetInterfaces().Where(x => x.IsGenericType)
var returnType = handler.Method.ReturnType;
if (returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(Task<>))
{
returnType = returnType.GenericTypeArguments.First();
}

var isCommand = returnType.GetInterfaces().Where(x => x.IsGenericType)
.Any(x => CommandTypes.Contains(x.GetGenericTypeDefinition()));
if (isCommand == false)
{
throw new ArgumentException(
"handler does not return command, check if delegate returns type that implements ICommand<> or ICommand<,>");
}

return returnType;
}

private static Type EnsureReturnTypeIsQuery(Delegate handler)
{
var returnType = handler.Method.ReturnType;
if (returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(Task<>))
{
returnType = returnType.GenericTypeArguments.First();
}

var isCommand = returnType.GetInterfaces().Where(x => x.IsGenericType)
.Any(x => QueryTypes.Contains(x.GetGenericTypeDefinition()));
if (isCommand == false)
{
throw new ArgumentException(
"handler does not return query, check if delegate returns type that implements IQuery<>");
}

return returnType;
}

private static List<T[]> GetNotEmptySubsets<T>(ICollection<T> items)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="8.0.4" />
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="8.0.5" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Cnblogs.Architecture.Ddd.Cqrs.AspNetCore\CqrsHeaderNames.cs">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="ClickHouse.Client" Version="7.4.1" />
<PackageReference Include="ClickHouse.Client" Version="7.5.0" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.5" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<ItemGroup>
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.1" />
</ItemGroup>

<ItemGroup>
Expand Down
15 changes: 12 additions & 3 deletions test/Cnblogs.Architecture.IntegrationTestProject/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Cnblogs.Architecture.IntegrationTestProject.Application.Queries;
using Cnblogs.Architecture.IntegrationTestProject.Payloads;
using Cnblogs.Architecture.TestIntegrationEvents;
using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

Expand Down Expand Up @@ -36,10 +37,18 @@

var apis = app.NewVersionedApi();
var v1 = apis.MapGroup("/api/v{version:apiVersion}").HasApiVersion(1);
v1.MapQuery<GetStringQuery>("apps/{appId}/strings/{stringId:int}/value", MapNullableRouteParameter.Enable, enableHead: true);
v1.MapQuery<GetStringQuery>("strings/{id:int}");
v1.MapQuery<GetStringQuery>(
"apps/{appId}/strings/{stringId:int}/value",
MapNullableRouteParameter.Enable,
enableHead: true);
v1.MapQuery(
"strings/{stringId:int}",
async (int stringId, [FromQuery] bool found = true)
=> await Task.FromResult(new GetStringQuery(StringId: stringId, Found: found)));
v1.MapQuery<ListStringsQuery>("strings");
v1.MapCommand("strings", (CreatePayload payload) => new CreateCommand(payload.NeedError, payload.Data));
v1.MapCommand(
"strings",
(CreatePayload payload) => Task.FromResult(new CreateCommand(payload.NeedError, payload.Data)));
v1.MapCommand(
"strings/{id:int}",
(int id, UpdatePayload payload) => new UpdateCommand(id, payload.NeedValidationError, payload.NeedExecutionError));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<PackageReference Include="Cnblogs.Serilog.Extensions" Version="1.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.4" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.5" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageReference Include="xunit" Version="2.8.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.0">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.5" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageReference Include="xunit" Version="2.8.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.0">
Expand Down

0 comments on commit 1b061fe

Please sign in to comment.