diff --git a/src/WebUI/Endpoints/TodoListEndpoints.cs b/src/WebUI/Endpoints/TodoListEndpoints.cs deleted file mode 100644 index eef01346e..000000000 --- a/src/WebUI/Endpoints/TodoListEndpoints.cs +++ /dev/null @@ -1,59 +0,0 @@ -using CleanArchitecture.Application.TodoLists.Commands.CreateTodoList; -using CleanArchitecture.Application.TodoLists.Commands.DeleteTodoList; -using CleanArchitecture.Application.TodoLists.Commands.UpdateTodoList; -using CleanArchitecture.Application.TodoLists.Queries.ExportTodos; -using CleanArchitecture.Application.TodoLists.Queries.GetTodos; -using CleanArchitecture.WebUI.Filters; - -namespace CleanArchitecture.WebUI.Endpoints; - -public class TodoListEndpoints -{ - private const string GroupName = "TodoLists"; - - public static void Map(WebApplication app) - { - var group = app - .MapGroup($"/api/{GroupName}") - .WithTags(GroupName) - .RequireAuthorization() - .WithOpenApi() - .AddEndpointFilter(); - - group - .MapGet("/", async (ISender sender) => await sender.Send(new GetTodosQuery())) - .WithName($"{GroupName}_GetTodoLists"); - - group - .MapGet("/Export/{id}", - async (ISender sender, int id) => - { - var vm = await sender.Send(new ExportTodosQuery { ListId = id }); - return Results.File(vm.Content, vm.ContentType, vm.FileName); - }) - .WithName($"{GroupName}_ExportTodos"); ; - - group - .MapPost("/", async (ISender sender, CreateTodoListCommand command) => await sender.Send(command)) - .WithName($"{GroupName}_CreateTodoList"); ; - - group - .MapPut("/{id}", - async (ISender sender, int id, UpdateTodoListCommand command) => - { - if (id != command.Id) return Results.BadRequest(); - await sender.Send(command); - return Results.NoContent(); - }) - .WithName($"{GroupName}_UpdateTodoList"); ; - - group - .MapDelete("/{id}", - async (ISender sender, int id) => - { - await sender.Send(new DeleteTodoListCommand(id)); - return Results.NoContent(); - }) - .WithName($"{GroupName}_DeleteTodoList"); ; - } -} diff --git a/src/WebUI/Endpoints/TodoListsEndpointGroup.cs b/src/WebUI/Endpoints/TodoListsEndpointGroup.cs new file mode 100644 index 000000000..3872bcfc3 --- /dev/null +++ b/src/WebUI/Endpoints/TodoListsEndpointGroup.cs @@ -0,0 +1,43 @@ +using CleanArchitecture.Application.TodoLists.Commands.CreateTodoList; +using CleanArchitecture.Application.TodoLists.Commands.DeleteTodoList; +using CleanArchitecture.Application.TodoLists.Commands.UpdateTodoList; +using CleanArchitecture.Application.TodoLists.Queries.ExportTodos; +using CleanArchitecture.Application.TodoLists.Queries.GetTodos; + +namespace CleanArchitecture.WebUI.Endpoints; + +public class TodoListsEndpointGroup : EndpointGroupBase +{ + public override void Map(WebApplication app) + { + MapGroup("TodoLists", app); + + MapGet("GetTodoLists", + async (ISender sender) => await sender.Send(new GetTodosQuery())); + + MapGet("ExportTodos", "Export/{id}", + async (ISender sender, int id) => + { + var vm = await sender.Send(new ExportTodosQuery { ListId = id }); + return Results.File(vm.Content, vm.ContentType, vm.FileName); + }); + + MapPost("CreateTodoList", + async (ISender sender, CreateTodoListCommand command) => await sender.Send(command)); + + MapPut("UpdateTodoList", "{id}", + async (ISender sender, int id, UpdateTodoListCommand command) => + { + if (id != command.Id) return Results.BadRequest(); + await sender.Send(command); + return Results.NoContent(); + }); + + MapDelete("DeleteTodoList", "{id}", + async (ISender sender, int id) => + { + await sender.Send(new DeleteTodoListCommand(id)); + return Results.NoContent(); + }); + } +} diff --git a/src/WebUI/Infrastructure/EndPointBase.cs b/src/WebUI/Infrastructure/EndPointBase.cs index de1f13085..26de0144b 100644 --- a/src/WebUI/Infrastructure/EndPointBase.cs +++ b/src/WebUI/Infrastructure/EndPointBase.cs @@ -1,6 +1,6 @@ namespace CleanArchitecture.WebUI.Infrastructure; -public abstract class EndPointBase : IEndpoint +public abstract class EndpointBase : IEndpoint { public abstract void Map(WebApplication app); diff --git a/src/WebUI/Infrastructure/EndpointGroupBase.cs b/src/WebUI/Infrastructure/EndpointGroupBase.cs new file mode 100644 index 000000000..32440c3aa --- /dev/null +++ b/src/WebUI/Infrastructure/EndpointGroupBase.cs @@ -0,0 +1,60 @@ +using CleanArchitecture.WebUI.Filters; + +namespace CleanArchitecture.WebUI.Infrastructure; + +public abstract class EndpointGroupBase +{ + private string? _groupName; + private RouteGroupBuilder? _group; + + public abstract void Map(WebApplication app); + + protected void MapGroup(string groupName, WebApplication app) + { + _groupName = groupName; + + _group = app + .MapGroup($"/api/{_groupName}") + .WithGroupName(_groupName) + .WithTags(_groupName) + .RequireAuthorization() + .WithOpenApi() + .AddEndpointFilter(); + } + + protected void MapGet(string name, Delegate handler) + { + MapGet(name, "", handler); + } + + protected void MapGet(string name, string prefix, Delegate handler) + { + _group!.MapGet(prefix, handler) + .WithName(GetEndpointName(name)); + } + + protected void MapPost(string name, Delegate handler) + { + MapPost(name, "", handler); + } + + protected void MapPost(string name, string prefix, Delegate handler) + { + _group!.MapPost(prefix, handler) + .WithName(GetEndpointName(name)); + } + + protected void MapPut(string name, string prefix, Delegate handler) + { + _group!.MapPut(prefix, handler) + .WithName(GetEndpointName(name)); + } + + protected void MapDelete(string name, string prefix, Delegate handler) + { + _group!.MapDelete(prefix, handler) + .WithName(GetEndpointName(name)); + } + + private string GetEndpointName(string name) => $"{_groupName}_{name}"; +} diff --git a/src/WebUI/Infrastructure/RouteHandlerBuilderExtensions.cs b/src/WebUI/Infrastructure/RouteHandlerBuilderExtensions.cs index 8b42f5e22..4c1675453 100644 --- a/src/WebUI/Infrastructure/RouteHandlerBuilderExtensions.cs +++ b/src/WebUI/Infrastructure/RouteHandlerBuilderExtensions.cs @@ -4,7 +4,7 @@ namespace CleanArchitecture.WebUI.Infrastructure; public static class RouteHandlerBuilderExtensions { - public static RouteHandlerBuilder WithDefaults(this RouteHandlerBuilder builder, EndPointBase endpoint) + public static RouteHandlerBuilder WithDefaults(this RouteHandlerBuilder builder, EndpointBase endpoint) { return builder .WithName($"{endpoint.Group}_{endpoint.Name}") diff --git a/src/WebUI/Infrastructure/WebApplicationExtensions.cs b/src/WebUI/Infrastructure/WebApplicationExtensions.cs index c86c9afb2..8046688f5 100644 --- a/src/WebUI/Infrastructure/WebApplicationExtensions.cs +++ b/src/WebUI/Infrastructure/WebApplicationExtensions.cs @@ -6,13 +6,13 @@ public static class WebApplicationExtensions { public static WebApplication MapEndpoints(this WebApplication app) { - var endPointType = typeof(IEndpoint); + var endpointType = typeof(IEndpoint); var assembly = Assembly.GetExecutingAssembly(); var endpointTypes = assembly.GetExportedTypes() .Where(t => t.IsAbstract == false && - t.GetInterfaces().Contains(endPointType)); + t.GetInterfaces().Contains(endpointType)); foreach (var type in endpointTypes) { @@ -24,4 +24,24 @@ public static WebApplication MapEndpoints(this WebApplication app) return app; } + + public static WebApplication MapEndpointGroups(this WebApplication app) + { + var endpointGroupType = typeof(EndpointGroupBase); + + var assembly = Assembly.GetExecutingAssembly(); + + var endpointGroupTypes = assembly.GetExportedTypes() + .Where(t => t.IsSubclassOf(endpointGroupType)); + + foreach (var type in endpointGroupTypes) + { + if (Activator.CreateInstance(type) is EndpointGroupBase instance) + { + instance.Map(app); + } + } + + return app; + } } diff --git a/src/WebUI/Program.cs b/src/WebUI/Program.cs index 3eea3f52a..ee11c2e16 100644 --- a/src/WebUI/Program.cs +++ b/src/WebUI/Program.cs @@ -1,5 +1,4 @@ using CleanArchitecture.Infrastructure.Persistence; -using CleanArchitecture.WebUI.Endpoints; var builder = WebApplication.CreateBuilder(args); @@ -48,6 +47,6 @@ app.MapFallbackToFile("index.html"); app.MapEndpoints(); -TodoListEndpoints.Map(app); +app.MapEndpointGroups(); app.Run(); diff --git a/src/WebUI/TodoItems/CreateTodoItem.cs b/src/WebUI/TodoItems/CreateTodoItem.cs index 0a30a6f82..c79d65065 100644 --- a/src/WebUI/TodoItems/CreateTodoItem.cs +++ b/src/WebUI/TodoItems/CreateTodoItem.cs @@ -2,7 +2,7 @@ namespace CleanArchitecture.WebUI.TodoItems; -public class CreateTodoItem : EndPointBase +public class CreateTodoItem : EndpointBase { public override void Map(WebApplication app) { diff --git a/src/WebUI/TodoItems/DeleteTodoItem.cs b/src/WebUI/TodoItems/DeleteTodoItem.cs index 7bbc4f3d7..c790d7e38 100644 --- a/src/WebUI/TodoItems/DeleteTodoItem.cs +++ b/src/WebUI/TodoItems/DeleteTodoItem.cs @@ -2,7 +2,7 @@ namespace CleanArchitecture.WebUI.TodoItems; -public class DeleteTodoItem : EndPointBase +public class DeleteTodoItem : EndpointBase { public override void Map(WebApplication app) { diff --git a/src/WebUI/TodoItems/UpdateTodoItem.cs b/src/WebUI/TodoItems/UpdateTodoItem.cs index a868a68f0..55712f2f0 100644 --- a/src/WebUI/TodoItems/UpdateTodoItem.cs +++ b/src/WebUI/TodoItems/UpdateTodoItem.cs @@ -2,7 +2,7 @@ namespace CleanArchitecture.WebUI.TodoItems; -public class UpdateTodoItem : EndPointBase +public class UpdateTodoItem : EndpointBase { public override void Map(WebApplication app) { diff --git a/src/WebUI/TodoItems/UpdateTodoItemDetail.cs b/src/WebUI/TodoItems/UpdateTodoItemDetail.cs index 21ec728c0..45a0a16b0 100644 --- a/src/WebUI/TodoItems/UpdateTodoItemDetail.cs +++ b/src/WebUI/TodoItems/UpdateTodoItemDetail.cs @@ -2,7 +2,7 @@ namespace CleanArchitecture.WebUI.TodoItems; -public class UpdateTodoItemDetail : EndPointBase +public class UpdateTodoItemDetail : EndpointBase { public override void Map(WebApplication app) { diff --git a/src/WebUI/WeatherForecasts/GetWeatherForecasts.cs b/src/WebUI/WeatherForecasts/GetWeatherForecasts.cs index d5919bfa0..b60397b82 100644 --- a/src/WebUI/WeatherForecasts/GetWeatherForecasts.cs +++ b/src/WebUI/WeatherForecasts/GetWeatherForecasts.cs @@ -2,7 +2,7 @@ namespace CleanArchitecture.WebUI.WeatherForecasts; -public class GetWeatherForecasts : EndPointBase +public class GetWeatherForecasts : EndpointBase { public override void Map(WebApplication app) {