From 3ac8c24ea554c3a16816aab0943eb3628ef7a53c Mon Sep 17 00:00:00 2001 From: "BRUCE.L" Date: Mon, 30 Jan 2023 00:38:25 -0800 Subject: [PATCH 1/4] Add validators and tests --- src/Wax.Api/Filters/GlobalExceptionFilter.cs | 43 +++++++++---------- src/Wax.Core/Exceptions/BusinessException.cs | 6 +-- src/Wax.Core/Extensions/TypeExtensions.cs | 20 +++++++++ .../DeleteCustomerCommandValidator.cs | 14 ++++++ .../Validators/GetCustomerRequestValidator.cs | 13 ++++++ .../UpdateCustomerCommandValidator.cs | 16 +++++++ .../Wax.UnitTests/Mediators/MessageChecker.cs | 37 ++++++++++++++++ 7 files changed, 121 insertions(+), 28 deletions(-) create mode 100644 src/Wax.Core/Extensions/TypeExtensions.cs create mode 100644 src/Wax.Core/Validators/DeleteCustomerCommandValidator.cs create mode 100644 src/Wax.Core/Validators/GetCustomerRequestValidator.cs create mode 100644 src/Wax.Core/Validators/UpdateCustomerCommandValidator.cs create mode 100644 tests/Wax.UnitTests/Mediators/MessageChecker.cs diff --git a/src/Wax.Api/Filters/GlobalExceptionFilter.cs b/src/Wax.Api/Filters/GlobalExceptionFilter.cs index 3e7a535..3b749a6 100644 --- a/src/Wax.Api/Filters/GlobalExceptionFilter.cs +++ b/src/Wax.Api/Filters/GlobalExceptionFilter.cs @@ -1,5 +1,4 @@ -using System.Net; -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Wax.Core.Exceptions; @@ -39,28 +38,12 @@ private void HandleBusinessException(ExceptionContext context) { _logger.Warning(context.Exception.Message); - var problemDetails = new ValidationProblemDetails - { - Instance = context.HttpContext.Request.Path, - Status = StatusCodes.Status400BadRequest, - Detail = "Please refer to the errors property for additional details." - }; - - problemDetails.Errors.Add("BusinessValidations", new string[] { context.Exception.Message }); - - context.Result = new BadRequestObjectResult(problemDetails); - context.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest; - } - - private void HandleInternalServerError(ExceptionContext context) - { - _logger.Error(context.Exception, context.Exception.Message); - var problemDetails = new ProblemDetails { - Status = StatusCodes.Status500InternalServerError, - Title = "Internal error", - Detail = "An error occur.Try it again." + Status = StatusCodes.Status403Forbidden, + Title = "Business error", + Detail = context.Exception.Message, + Instance = context.HttpContext.Request.Path }; context.Result = new ObjectResult(problemDetails); @@ -76,7 +59,7 @@ private void HandleEntityNotFoundException(ExceptionContext context) { Status = StatusCodes.Status404NotFound, Title = "The specified resource was not found.", - Detail = exception.Message + Detail = exception.Message, }; context.Result = new NotFoundObjectResult(details); @@ -93,4 +76,18 @@ private void HandleValidationException(ExceptionContext context) context.ExceptionHandled = true; } + + private void HandleInternalServerError(ExceptionContext context) + { + _logger.Error(context.Exception, context.Exception.Message); + + var problemDetails = new ProblemDetails + { + Status = StatusCodes.Status500InternalServerError, + Title = "Internal error", + Detail = "An error occur.Try it again later." + }; + + context.Result = new ObjectResult(problemDetails); + } } \ No newline at end of file diff --git a/src/Wax.Core/Exceptions/BusinessException.cs b/src/Wax.Core/Exceptions/BusinessException.cs index 4a49fea..b58e243 100644 --- a/src/Wax.Core/Exceptions/BusinessException.cs +++ b/src/Wax.Core/Exceptions/BusinessException.cs @@ -2,11 +2,7 @@ { public class BusinessException : Exception { - public BusinessException() - { - } - - public BusinessException(string message) : base(message) + protected BusinessException(string message) : base(message) { } } diff --git a/src/Wax.Core/Extensions/TypeExtensions.cs b/src/Wax.Core/Extensions/TypeExtensions.cs new file mode 100644 index 0000000..badabcf --- /dev/null +++ b/src/Wax.Core/Extensions/TypeExtensions.cs @@ -0,0 +1,20 @@ +namespace Wax.Core.Extensions; + +public static class TypeExtensions +{ + public static bool IsAssignableToGenericType(this Type givenType, Type genericType) + { + var interfaceTypes = givenType.GetInterfaces(); + + if (interfaceTypes.Any(it => it.IsGenericType && it.GetGenericTypeDefinition() == genericType)) + { + return true; + } + + if (givenType.IsGenericType && givenType.GetGenericTypeDefinition() == genericType) + return true; + + var baseType = givenType.BaseType; + return baseType != null && IsAssignableToGenericType(baseType, genericType); + } +} \ No newline at end of file diff --git a/src/Wax.Core/Validators/DeleteCustomerCommandValidator.cs b/src/Wax.Core/Validators/DeleteCustomerCommandValidator.cs new file mode 100644 index 0000000..2d3d66f --- /dev/null +++ b/src/Wax.Core/Validators/DeleteCustomerCommandValidator.cs @@ -0,0 +1,14 @@ +using FluentValidation; +using Wax.Core.Processing.FluentMessageValidator; +using Wax.Messages.Commands.Customers; + +namespace Wax.Core.Validators; + + +public class DeleteCustomerCommandValidator : FluentMessageValidator +{ + public DeleteCustomerCommandValidator() + { + RuleFor(v => v.CustomerId).NotEmpty(); + } +} \ No newline at end of file diff --git a/src/Wax.Core/Validators/GetCustomerRequestValidator.cs b/src/Wax.Core/Validators/GetCustomerRequestValidator.cs new file mode 100644 index 0000000..f2436d0 --- /dev/null +++ b/src/Wax.Core/Validators/GetCustomerRequestValidator.cs @@ -0,0 +1,13 @@ +using FluentValidation; +using Wax.Core.Processing.FluentMessageValidator; +using Wax.Messages.Requests.Customers; + +namespace Wax.Core.Validators; + +public class GetCustomerRequestValidator : FluentMessageValidator +{ + public GetCustomerRequestValidator() + { + RuleFor(v => v.CustomerId).NotEmpty(); + } +} \ No newline at end of file diff --git a/src/Wax.Core/Validators/UpdateCustomerCommandValidator.cs b/src/Wax.Core/Validators/UpdateCustomerCommandValidator.cs new file mode 100644 index 0000000..05381ab --- /dev/null +++ b/src/Wax.Core/Validators/UpdateCustomerCommandValidator.cs @@ -0,0 +1,16 @@ +using FluentValidation; +using Wax.Core.Processing.FluentMessageValidator; +using Wax.Messages.Commands.Customers; + +namespace Wax.Core.Validators; + +public class UpdateCustomerCommandValidator : FluentMessageValidator +{ + public UpdateCustomerCommandValidator() + { + RuleFor(v => v.CustomerId).NotEmpty(); + RuleFor(v => v.Name).NotEmpty().MaximumLength(64); + RuleFor(v => v.Address).MaximumLength(512); + RuleFor(v => v.Contact).MaximumLength(128); + } +} \ No newline at end of file diff --git a/tests/Wax.UnitTests/Mediators/MessageChecker.cs b/tests/Wax.UnitTests/Mediators/MessageChecker.cs new file mode 100644 index 0000000..a163e59 --- /dev/null +++ b/tests/Wax.UnitTests/Mediators/MessageChecker.cs @@ -0,0 +1,37 @@ +using System.Linq; +using Mediator.Net.Contracts; +using Shouldly; +using Wax.Core; +using Wax.Core.Processing.FluentMessageValidator; +using Wax.Messages.Commands.Customers; +using Xunit; + +namespace Wax.UnitTests.Mediators; + +public class MessageChecker +{ + [Fact] + public void ShouldAllMessageHaveValidators() + { + var messageTypes = typeof(CreateCustomerCommand).Assembly.GetTypes() + .Where(t => t.IsAssignableTo(typeof(IRequest)) || t.IsAssignableTo(typeof(ICommand))) + .Where(t => !t.IsAbstract && !t.IsInterface) + .ToList(); + + var validatorTypes = typeof(ApplicationModule).Assembly.GetTypes() + .Where(t => !t.IsAbstract && !t.IsInterface) + .Where(t => t.BaseType is { IsGenericType: true } && + t.BaseType.GetGenericTypeDefinition() == typeof(FluentMessageValidator<>)).ToList(); + + var noValidatorMessageTypes = (from messageType in messageTypes + let hasValidator = + validatorTypes.Any(x => x.BaseType != null && x.BaseType.GenericTypeArguments[0] == messageType) + where !hasValidator + select messageType).ToList(); + + noValidatorMessageTypes.ShouldBeEmpty( + $"[{string.Join(", ", noValidatorMessageTypes.Select(t => t.FullName))}] has no validators."); + + messageTypes.ShouldNotBeNull(); + } +} \ No newline at end of file From 2820b12349ddb290ee296eec0c413e69cd1115e7 Mon Sep 17 00:00:00 2001 From: "BRUCE.L" Date: Mon, 30 Jan 2023 22:20:01 -0800 Subject: [PATCH 2/4] Update --- src/Wax.Api/Program.cs | 50 +++++++++++++++++-- src/Wax.Api/Startup.cs | 16 ++---- src/Wax.Api/Wax.Api.csproj | 4 +- src/Wax.Api/appsettings.json | 32 ++---------- src/Wax.Core/ApplicationModule.cs | 5 +- src/Wax.Core/Extensions/TypeExtensions.cs | 22 ++++++++ .../IFluentMessageValidator.cs | 2 +- .../MessageValidatorMiddleware.cs | 4 +- .../MessageValidatorSpecification.cs} | 8 +-- .../Middlewares/Logging/LoggerMiddleware.cs | 25 ++++++++++ .../Logging/LoggerSpecification.cs | 50 +++++++++++++++++++ .../Processing/AllConstructorFinder.cs | 20 -------- .../CreateCustomerCommandValidator.cs | 2 +- .../DeleteCustomerCommandValidator.cs | 2 +- .../Validators/GetCustomerRequestValidator.cs | 2 +- .../UpdateCustomerCommandValidator.cs | 2 +- .../Wax.UnitTests/Mediators/MessageChecker.cs | 6 +-- 17 files changed, 167 insertions(+), 85 deletions(-) rename src/Wax.Core/{Processing => Middlewares}/FluentMessageValidator/IFluentMessageValidator.cs (94%) rename src/Wax.Core/{Processing => Middlewares}/FluentMessageValidator/MessageValidatorMiddleware.cs (84%) rename src/Wax.Core/{Processing/FluentMessageValidator/MessageValidatorMiddlewareSpecification.cs => Middlewares/FluentMessageValidator/MessageValidatorSpecification.cs} (81%) create mode 100644 src/Wax.Core/Middlewares/Logging/LoggerMiddleware.cs create mode 100644 src/Wax.Core/Middlewares/Logging/LoggerSpecification.cs delete mode 100644 src/Wax.Core/Processing/AllConstructorFinder.cs diff --git a/src/Wax.Api/Program.cs b/src/Wax.Api/Program.cs index 3702427..a80b572 100644 --- a/src/Wax.Api/Program.cs +++ b/src/Wax.Api/Program.cs @@ -3,21 +3,27 @@ namespace Wax.Api; -public class Program +public partial class Program { public static void Main(string[] args) { - var application = typeof(Program).Namespace ?? "Wax.Api"; - + var configuration = GetConfiguration(); + + Log.Logger = CreateSerilogLogger(configuration); + try { + Log.Information("Configuring api host ({ApplicationContext})... ", AppName); + var webHost = CreateWebHostBuilder(args).Build(); + + Log.Information("Starting api host ({ApplicationContext})...", AppName); webHost.Run(); } catch (Exception ex) { - Log.Fatal(ex, "Program terminated unexpectedly ({ApplicationContext})!", application); + Log.Fatal(ex, "Program terminated unexpectedly!"); } finally { @@ -27,6 +33,40 @@ public static void Main(string[] args) private static IHostBuilder CreateWebHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) + .ConfigureLogging(l => l.AddSerilog(Log.Logger)) .UseServiceProviderFactory(new AutofacServiceProviderFactory()) - .ConfigureWebHostDefaults(builder => { builder.UseStartup(); }); + .ConfigureWebHostDefaults(builder => { builder.UseStartup(); }).UseSerilog(); + + private static IConfiguration GetConfiguration() + { + var builder = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json", false, true) + .AddEnvironmentVariables(); + + return builder.Build(); + } + + private static Serilog.ILogger CreateSerilogLogger(IConfiguration configuration) + { + var seqServerUrl = configuration["Serilog:Seq:ServerUrl"]; + var seqApiKey = configuration["Serilog:Seq:ApiKey"]; + + return new LoggerConfiguration() + .MinimumLevel.Information() + .Enrich.WithProperty("ApplicationContext", AppName) + .Enrich.FromLogContext() + .Enrich.WithCorrelationId() + .WriteTo.Console() + .WriteTo.Seq(seqServerUrl, apiKey: seqApiKey) + .CreateLogger(); + } +} + +public partial class Program +{ + private static readonly string Namespace = typeof(Startup).Namespace; + + private static readonly string AppName = + Namespace[(Namespace.LastIndexOf('.', Namespace.LastIndexOf('.') - 1) + 1)..]; } \ No newline at end of file diff --git a/src/Wax.Api/Startup.cs b/src/Wax.Api/Startup.cs index f95ae6a..c360a6a 100644 --- a/src/Wax.Api/Startup.cs +++ b/src/Wax.Api/Startup.cs @@ -17,26 +17,18 @@ public class Startup public Startup(IConfiguration configuration) { Configuration = configuration; - - Log.Logger = new LoggerConfiguration() - .ReadFrom.Configuration(Configuration) - .CreateLogger(); - - Log.Information("Starting up"); } // ConfigureServices is where you register dependencies. This gets // called by the runtime before the ConfigureContainer method, below. public void ConfigureServices(IServiceCollection services) { - services.AddControllers(options => - { - options.Filters.Add(); - }); + services.AddControllers(options => { options.Filters.Add(); }); services.AddOptions(); services.AddCustomSwagger(); services.AddScoped(); services.AddLogging(); + services.AddHttpContextAccessor(); _serviceCollection = services; } @@ -62,10 +54,8 @@ public void ConfigureContainer(ContainerBuilder builder) // Configure is where you add middleware. This is called after // ConfigureContainer. You can use IApplicationBuilder.ApplicationServices // here if you need to resolve things from the container. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory) + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { - loggerFactory.AddSerilog(); - if (env.IsDevelopment()) { app.UseSwagger(); diff --git a/src/Wax.Api/Wax.Api.csproj b/src/Wax.Api/Wax.Api.csproj index da072b6..ffa237a 100644 --- a/src/Wax.Api/Wax.Api.csproj +++ b/src/Wax.Api/Wax.Api.csproj @@ -6,10 +6,10 @@ + - - + diff --git a/src/Wax.Api/appsettings.json b/src/Wax.Api/appsettings.json index 0a27a7e..df6d748 100644 --- a/src/Wax.Api/appsettings.json +++ b/src/Wax.Api/appsettings.json @@ -1,35 +1,9 @@ { "AllowedHosts": "*", "Serilog": { - "Using": [ - "Serilog.Sinks.Console" - ], - "MinimumLevel": { - "Default": "Debug", - "Override": { - "Microsoft": "Warning", - "System": "Warning" - } - }, - "WriteTo": [ - { - "Name": "Console" - }, - { - "Name": "Seq", - "Args": { - "serverUrl": "http://localhost:5341" - } - } - ], - "Enrich": [ - "FromLogContext", - "WithMachineName", - "WithThreadId", - "WithCorrelationId" - ], - "Properties": { - "Application": "Wax.Api" + "Seq": { + "ServerUrl": "http://localhost:5341", + "ApiKey": "" } }, "ConnectionStrings": { diff --git a/src/Wax.Core/ApplicationModule.cs b/src/Wax.Core/ApplicationModule.cs index 9011722..b2b82ed 100644 --- a/src/Wax.Core/ApplicationModule.cs +++ b/src/Wax.Core/ApplicationModule.cs @@ -11,7 +11,8 @@ using Wax.Core.DependencyInjection; using Wax.Core.Domain; using Wax.Core.Domain.Customers; -using Wax.Core.Processing.FluentMessageValidator; +using Wax.Core.Middlewares.FluentMessageValidator; +using Wax.Core.Middlewares.Logging; using Wax.Core.Services.Identity; using Module = Autofac.Module; @@ -112,7 +113,7 @@ private void RegisterMediator(ContainerBuilder builder) mediatorBuilder.RegisterHandlers(_assemblies); mediatorBuilder.ConfigureGlobalReceivePipe(c => { - c.UseSerilog(logger: _logger); + c.UseLogger(); c.UseMessageValidator(); }); diff --git a/src/Wax.Core/Extensions/TypeExtensions.cs b/src/Wax.Core/Extensions/TypeExtensions.cs index badabcf..a02a179 100644 --- a/src/Wax.Core/Extensions/TypeExtensions.cs +++ b/src/Wax.Core/Extensions/TypeExtensions.cs @@ -17,4 +17,26 @@ public static bool IsAssignableToGenericType(this Type givenType, Type genericTy var baseType = givenType.BaseType; return baseType != null && IsAssignableToGenericType(baseType, genericType); } + + public static string GetGenericTypeName(this Type type) + { + string typeName; + + if (type.IsGenericType) + { + var genericTypes = string.Join(",", type.GetGenericArguments().Select(t => t.Name).ToArray()); + typeName = $"{type.Name.Remove(type.Name.IndexOf('`'))}<{genericTypes}>"; + } + else + { + typeName = type.Name; + } + + return typeName; + } + + public static string GetGenericTypeName(this object @object) + { + return @object.GetType().GetGenericTypeName(); + } } \ No newline at end of file diff --git a/src/Wax.Core/Processing/FluentMessageValidator/IFluentMessageValidator.cs b/src/Wax.Core/Middlewares/FluentMessageValidator/IFluentMessageValidator.cs similarity index 94% rename from src/Wax.Core/Processing/FluentMessageValidator/IFluentMessageValidator.cs rename to src/Wax.Core/Middlewares/FluentMessageValidator/IFluentMessageValidator.cs index 2de9177..c154763 100644 --- a/src/Wax.Core/Processing/FluentMessageValidator/IFluentMessageValidator.cs +++ b/src/Wax.Core/Middlewares/FluentMessageValidator/IFluentMessageValidator.cs @@ -2,7 +2,7 @@ using FluentValidation.Results; using Mediator.Net.Contracts; -namespace Wax.Core.Processing.FluentMessageValidator; +namespace Wax.Core.Middlewares.FluentMessageValidator; public interface IFluentMessageValidator { diff --git a/src/Wax.Core/Processing/FluentMessageValidator/MessageValidatorMiddleware.cs b/src/Wax.Core/Middlewares/FluentMessageValidator/MessageValidatorMiddleware.cs similarity index 84% rename from src/Wax.Core/Processing/FluentMessageValidator/MessageValidatorMiddleware.cs rename to src/Wax.Core/Middlewares/FluentMessageValidator/MessageValidatorMiddleware.cs index c8be633..cfd5309 100644 --- a/src/Wax.Core/Processing/FluentMessageValidator/MessageValidatorMiddleware.cs +++ b/src/Wax.Core/Middlewares/FluentMessageValidator/MessageValidatorMiddleware.cs @@ -3,7 +3,7 @@ using Mediator.Net.Contracts; using Mediator.Net.Pipeline; -namespace Wax.Core.Processing.FluentMessageValidator; +namespace Wax.Core.Middlewares.FluentMessageValidator; public static class MessageValidatorMiddleware { @@ -19,6 +19,6 @@ public static void UseMessageValidator(this IPipeConfigurator>(); - configurator.AddPipeSpecification(new MessageValidatorMiddlewareSpecification(messageValidators)); + configurator.AddPipeSpecification(new MessageValidatorSpecification(messageValidators)); } } \ No newline at end of file diff --git a/src/Wax.Core/Processing/FluentMessageValidator/MessageValidatorMiddlewareSpecification.cs b/src/Wax.Core/Middlewares/FluentMessageValidator/MessageValidatorSpecification.cs similarity index 81% rename from src/Wax.Core/Processing/FluentMessageValidator/MessageValidatorMiddlewareSpecification.cs rename to src/Wax.Core/Middlewares/FluentMessageValidator/MessageValidatorSpecification.cs index cfd8309..6bbd3f2 100644 --- a/src/Wax.Core/Processing/FluentMessageValidator/MessageValidatorMiddlewareSpecification.cs +++ b/src/Wax.Core/Middlewares/FluentMessageValidator/MessageValidatorSpecification.cs @@ -3,14 +3,14 @@ using Mediator.Net.Contracts; using Mediator.Net.Pipeline; -namespace Wax.Core.Processing.FluentMessageValidator; +namespace Wax.Core.Middlewares.FluentMessageValidator; -public class MessageValidatorMiddlewareSpecification : IPipeSpecification +public class MessageValidatorSpecification : IPipeSpecification where TContext : IContext { private readonly IEnumerable _messageValidators; - public MessageValidatorMiddlewareSpecification(IEnumerable messageValidators) + public MessageValidatorSpecification(IEnumerable messageValidators) { _messageValidators = messageValidators; } @@ -46,6 +46,6 @@ public Task AfterExecute(TContext context, CancellationToken cancellationToken) public Task OnException(Exception ex, TContext context) { ExceptionDispatchInfo.Capture(ex).Throw(); - throw ex; + return Task.CompletedTask; } } \ No newline at end of file diff --git a/src/Wax.Core/Middlewares/Logging/LoggerMiddleware.cs b/src/Wax.Core/Middlewares/Logging/LoggerMiddleware.cs new file mode 100644 index 0000000..a26822d --- /dev/null +++ b/src/Wax.Core/Middlewares/Logging/LoggerMiddleware.cs @@ -0,0 +1,25 @@ +using Mediator.Net; +using Mediator.Net.Context; +using Mediator.Net.Contracts; +using Mediator.Net.Pipeline; +using Serilog; + +namespace Wax.Core.Middlewares.Logging; + +public static class LoggerMiddleware +{ + public static void UseLogger(this IPipeConfigurator configurator, + ILogger logger = null) + where TContext : IContext + { + if (logger == null && configurator.DependencyScope == null) + { + throw new DependencyScopeNotConfiguredException( + $"{nameof(logger)} is not provided and IDependencyScope is not configured, Please ensure {nameof(logger)} is registered properly if you are using IoC container, otherwise please pass {nameof(logger)} as parameter"); + } + + logger ??= configurator.DependencyScope.Resolve(); + + configurator.AddPipeSpecification(new LoggerSpecification(logger)); + } +} \ No newline at end of file diff --git a/src/Wax.Core/Middlewares/Logging/LoggerSpecification.cs b/src/Wax.Core/Middlewares/Logging/LoggerSpecification.cs new file mode 100644 index 0000000..8781cb0 --- /dev/null +++ b/src/Wax.Core/Middlewares/Logging/LoggerSpecification.cs @@ -0,0 +1,50 @@ +using System.Runtime.ExceptionServices; +using Mediator.Net.Context; +using Mediator.Net.Contracts; +using Mediator.Net.Pipeline; +using Serilog; +using Wax.Core.Extensions; + +namespace Wax.Core.Middlewares.Logging; + +public class LoggerSpecification : IPipeSpecification + where TContext : IContext +{ + private readonly ILogger _logger; + + public LoggerSpecification(ILogger logger) + { + _logger = logger; + } + + public bool ShouldExecute(TContext context, CancellationToken cancellationToken) + { + return true; + } + + public Task BeforeExecute(TContext context, CancellationToken cancellationToken) + { + _logger.Information("----- Handling message {MessageName} ({@Message})", context.Message.GetGenericTypeName(), + context.Message); + return Task.CompletedTask; + } + + public Task Execute(TContext context, CancellationToken cancellationToken) + { + return Task.CompletedTask; + } + + public Task AfterExecute(TContext context, CancellationToken cancellationToken) + { + _logger.Information("----- Message {MessageName} handled - response: {@Response}", + context.Message.GetGenericTypeName(), context.Result); + + return Task.CompletedTask; + } + + public Task OnException(Exception ex, TContext context) + { + ExceptionDispatchInfo.Capture(ex).Throw(); + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/src/Wax.Core/Processing/AllConstructorFinder.cs b/src/Wax.Core/Processing/AllConstructorFinder.cs deleted file mode 100644 index 4ae53bc..0000000 --- a/src/Wax.Core/Processing/AllConstructorFinder.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Collections.Concurrent; -using System.Reflection; -using Autofac.Core.Activators.Reflection; - -namespace Wax.Core.Processing; - -internal class AllConstructorFinder : IConstructorFinder -{ - private static readonly ConcurrentDictionary Cache = - new ConcurrentDictionary(); - - - public ConstructorInfo[] FindConstructors(Type targetType) - { - var result = Cache.GetOrAdd(targetType, - t => t.GetTypeInfo().DeclaredConstructors.ToArray()); - - return result.Length > 0 ? result : throw new NoConstructorsFoundException(targetType); - } -} \ No newline at end of file diff --git a/src/Wax.Core/Validators/CreateCustomerCommandValidator.cs b/src/Wax.Core/Validators/CreateCustomerCommandValidator.cs index b4cdab6..7307de0 100644 --- a/src/Wax.Core/Validators/CreateCustomerCommandValidator.cs +++ b/src/Wax.Core/Validators/CreateCustomerCommandValidator.cs @@ -1,5 +1,5 @@ using FluentValidation; -using Wax.Core.Processing.FluentMessageValidator; +using Wax.Core.Middlewares.FluentMessageValidator; using Wax.Messages.Commands.Customers; namespace Wax.Core.Validators; diff --git a/src/Wax.Core/Validators/DeleteCustomerCommandValidator.cs b/src/Wax.Core/Validators/DeleteCustomerCommandValidator.cs index 2d3d66f..d1b78e8 100644 --- a/src/Wax.Core/Validators/DeleteCustomerCommandValidator.cs +++ b/src/Wax.Core/Validators/DeleteCustomerCommandValidator.cs @@ -1,5 +1,5 @@ using FluentValidation; -using Wax.Core.Processing.FluentMessageValidator; +using Wax.Core.Middlewares.FluentMessageValidator; using Wax.Messages.Commands.Customers; namespace Wax.Core.Validators; diff --git a/src/Wax.Core/Validators/GetCustomerRequestValidator.cs b/src/Wax.Core/Validators/GetCustomerRequestValidator.cs index f2436d0..18abf24 100644 --- a/src/Wax.Core/Validators/GetCustomerRequestValidator.cs +++ b/src/Wax.Core/Validators/GetCustomerRequestValidator.cs @@ -1,5 +1,5 @@ using FluentValidation; -using Wax.Core.Processing.FluentMessageValidator; +using Wax.Core.Middlewares.FluentMessageValidator; using Wax.Messages.Requests.Customers; namespace Wax.Core.Validators; diff --git a/src/Wax.Core/Validators/UpdateCustomerCommandValidator.cs b/src/Wax.Core/Validators/UpdateCustomerCommandValidator.cs index 05381ab..cce8d67 100644 --- a/src/Wax.Core/Validators/UpdateCustomerCommandValidator.cs +++ b/src/Wax.Core/Validators/UpdateCustomerCommandValidator.cs @@ -1,5 +1,5 @@ using FluentValidation; -using Wax.Core.Processing.FluentMessageValidator; +using Wax.Core.Middlewares.FluentMessageValidator; using Wax.Messages.Commands.Customers; namespace Wax.Core.Validators; diff --git a/tests/Wax.UnitTests/Mediators/MessageChecker.cs b/tests/Wax.UnitTests/Mediators/MessageChecker.cs index a163e59..db8d4c6 100644 --- a/tests/Wax.UnitTests/Mediators/MessageChecker.cs +++ b/tests/Wax.UnitTests/Mediators/MessageChecker.cs @@ -2,7 +2,8 @@ using Mediator.Net.Contracts; using Shouldly; using Wax.Core; -using Wax.Core.Processing.FluentMessageValidator; +using Wax.Core.Extensions; +using Wax.Core.Middlewares.FluentMessageValidator; using Wax.Messages.Commands.Customers; using Xunit; @@ -20,8 +21,7 @@ public void ShouldAllMessageHaveValidators() var validatorTypes = typeof(ApplicationModule).Assembly.GetTypes() .Where(t => !t.IsAbstract && !t.IsInterface) - .Where(t => t.BaseType is { IsGenericType: true } && - t.BaseType.GetGenericTypeDefinition() == typeof(FluentMessageValidator<>)).ToList(); + .Where(t => t.IsAssignableToGenericType(typeof(FluentMessageValidator<>))).ToList(); var noValidatorMessageTypes = (from messageType in messageTypes let hasValidator = From b151e6c1d4bc006b0d563cc9a0ff953865f69a25 Mon Sep 17 00:00:00 2001 From: "BRUCE.L" Date: Mon, 30 Jan 2023 23:10:14 -0800 Subject: [PATCH 3/4] Cleanup --- src/Wax.Core/ApplicationModule.cs | 23 +------------------ .../Repositories/EfCoreCustomerRepository.cs | 3 ++- src/Wax.Core/Wax.Core.csproj | 1 - 3 files changed, 3 insertions(+), 24 deletions(-) diff --git a/src/Wax.Core/ApplicationModule.cs b/src/Wax.Core/ApplicationModule.cs index b2b82ed..dd67cf9 100644 --- a/src/Wax.Core/ApplicationModule.cs +++ b/src/Wax.Core/ApplicationModule.cs @@ -3,14 +3,12 @@ using AutoMapper.Contrib.Autofac.DependencyInjection; using Mediator.Net; using Mediator.Net.Autofac; -using Mediator.Net.Middlewares.Serilog; using Microsoft.EntityFrameworkCore; using Serilog; using Wax.Core.Data; using Wax.Core.Data.Repositories; using Wax.Core.DependencyInjection; using Wax.Core.Domain; -using Wax.Core.Domain.Customers; using Wax.Core.Middlewares.FluentMessageValidator; using Wax.Core.Middlewares.Logging; using Wax.Core.Services.Identity; @@ -33,7 +31,7 @@ public ApplicationModule(ILogger logger, ICurrentUser currentUser, string connec _connectionString = connectionString; _assemblies = (assemblies ?? Array.Empty()) - .Concat(new[] {typeof(ApplicationModule).Assembly}) + .Concat(new[] { typeof(ApplicationModule).Assembly }) .ToArray(); } @@ -89,9 +87,6 @@ private void RegisterDatabase(ContainerBuilder builder) builder.RegisterGeneric(typeof(EfCoreRepository<>)) .As(typeof(IRepository<>)) .InstancePerLifetimeScope(); - - //TODO: Register all repository - builder.RegisterType().As().InstancePerLifetimeScope(); } private void RegisterIdentity(ContainerBuilder builder) @@ -126,21 +121,5 @@ private void RegisterValidator(ContainerBuilder builder) .Where(t => t.IsClass && typeof(IFluentMessageValidator).IsAssignableFrom(t)) .AsSelf().AsImplementedInterfaces(); } - - private static bool IsAssignableToGenericType(Type givenType, Type genericType) - { - var interfaceTypes = givenType.GetInterfaces(); - - if (interfaceTypes.Any(it => it.IsGenericType && it.GetGenericTypeDefinition() == genericType)) - { - return true; - } - - if (givenType.IsGenericType && givenType.GetGenericTypeDefinition() == genericType) - return true; - - var baseType = givenType.BaseType; - return baseType != null && IsAssignableToGenericType(baseType, genericType); - } } } \ No newline at end of file diff --git a/src/Wax.Core/Data/Repositories/EfCoreCustomerRepository.cs b/src/Wax.Core/Data/Repositories/EfCoreCustomerRepository.cs index 2892347..eaa0cbc 100644 --- a/src/Wax.Core/Data/Repositories/EfCoreCustomerRepository.cs +++ b/src/Wax.Core/Data/Repositories/EfCoreCustomerRepository.cs @@ -1,9 +1,10 @@ using Microsoft.EntityFrameworkCore; +using Wax.Core.DependencyInjection; using Wax.Core.Domain.Customers; namespace Wax.Core.Data.Repositories; -public class EfCoreCustomerRepository : EfCoreRepository, ICustomerRepository +public class EfCoreCustomerRepository : EfCoreRepository, ICustomerRepository, IScopedDependency { private readonly DbSet _customers; diff --git a/src/Wax.Core/Wax.Core.csproj b/src/Wax.Core/Wax.Core.csproj index eaa03e0..131af09 100644 --- a/src/Wax.Core/Wax.Core.csproj +++ b/src/Wax.Core/Wax.Core.csproj @@ -14,7 +14,6 @@ - From 60713686d3a55de73c9e7c0f3b4eb40ec4ccd824 Mon Sep 17 00:00:00 2001 From: "BRUCE.L" Date: Mon, 30 Jan 2023 23:47:46 -0800 Subject: [PATCH 4/4] Update README.md --- README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/README.md b/README.md index 6a05798..0c655ed 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,20 @@ # Wax + +[![.NET](https://github.com/sj-distributor/Wax/actions/workflows/dotnet.yml/badge.svg)](https://github.com/sj-distributor/Wax/actions/workflows/dotnet.yml) +[![Nuget](https://img.shields.io/nuget/v/Wax.Template)](https://www.nuget.org/packages/Wax.Template) + WilTechs Architecture Solution Template for .NET 6 + +## Getting Started + +Using dotnet cli template, install the template: + +``` +dotnet new -i Wax.Template +``` + +Run this command to create the solution: + +``` +dotnet new wax -o ProjectName +```