diff --git a/Hestia.Access/Hestia.Access.csproj b/Hestia.Access/Hestia.Access.csproj index a6def66..d680542 100644 --- a/Hestia.Access/Hestia.Access.csproj +++ b/Hestia.Access/Hestia.Access.csproj @@ -6,12 +6,9 @@ enable - - - - + diff --git a/Hestia.Access/Requests/Authentication/Commands/CreateTokenLog/CreateOrUpdateTokenLogCommand.cs b/Hestia.Access/Requests/Authentication/Commands/CreateTokenLog/CreateOrUpdateTokenLogCommand.cs index 104e27d..d21bfb3 100644 --- a/Hestia.Access/Requests/Authentication/Commands/CreateTokenLog/CreateOrUpdateTokenLogCommand.cs +++ b/Hestia.Access/Requests/Authentication/Commands/CreateTokenLog/CreateOrUpdateTokenLogCommand.cs @@ -1,4 +1,5 @@ -using MediatR; +using Hestia.Mediator.Infrastructure.Messaging; +using Hestia.Mediator.Infrastructure.Types; namespace Hestia.Access.Requests.Authentication.Commands.CreateTokenLog; diff --git a/Hestia.Access/Requests/Product/Commands/CreateProduct/CreateProductCommand.cs b/Hestia.Access/Requests/Product/Commands/CreateProduct/CreateProductCommand.cs index 53ce08a..031c457 100644 --- a/Hestia.Access/Requests/Product/Commands/CreateProduct/CreateProductCommand.cs +++ b/Hestia.Access/Requests/Product/Commands/CreateProduct/CreateProductCommand.cs @@ -1,4 +1,4 @@ -using MediatR; +using Hestia.Mediator.Infrastructure.Messaging; namespace Hestia.Access.Requests.Product.Commands.CreateProduct; diff --git a/Hestia.Access/Requests/Product/Commands/DeleteProduct/DeleteProductCommand.cs b/Hestia.Access/Requests/Product/Commands/DeleteProduct/DeleteProductCommand.cs index e24e544..b2c3893 100644 --- a/Hestia.Access/Requests/Product/Commands/DeleteProduct/DeleteProductCommand.cs +++ b/Hestia.Access/Requests/Product/Commands/DeleteProduct/DeleteProductCommand.cs @@ -1,4 +1,4 @@ -using MediatR; +using Hestia.Mediator.Infrastructure.Messaging; namespace Hestia.Access.Requests.Product.Commands.DeleteProduct; diff --git a/Hestia.Access/Requests/Product/Commands/UpdateProduct/UpdateProductCommand.cs b/Hestia.Access/Requests/Product/Commands/UpdateProduct/UpdateProductCommand.cs index a2ca5b8..d5b04e0 100644 --- a/Hestia.Access/Requests/Product/Commands/UpdateProduct/UpdateProductCommand.cs +++ b/Hestia.Access/Requests/Product/Commands/UpdateProduct/UpdateProductCommand.cs @@ -1,4 +1,4 @@ -using MediatR; +using Hestia.Mediator.Infrastructure.Messaging; namespace Hestia.Access.Requests.Product.Commands.UpdateProduct; diff --git a/Hestia.Access/Requests/Product/Queries/GetExisting/GetExistingProductByCompositeIdQuery.cs b/Hestia.Access/Requests/Product/Queries/GetExisting/GetExistingProductByCompositeIdQuery.cs index 31bf486..2237a36 100644 --- a/Hestia.Access/Requests/Product/Queries/GetExisting/GetExistingProductByCompositeIdQuery.cs +++ b/Hestia.Access/Requests/Product/Queries/GetExisting/GetExistingProductByCompositeIdQuery.cs @@ -1,4 +1,4 @@ -using MediatR; +using Hestia.Mediator.Infrastructure.Messaging; namespace Hestia.Access.Requests.Product.Queries.GetExisting; diff --git a/Hestia.Access/Requests/Product/Queries/GetExisting/GetExistingProductByExternalIdQuery.cs b/Hestia.Access/Requests/Product/Queries/GetExisting/GetExistingProductByExternalIdQuery.cs index 5e2909d..87ec6de 100644 --- a/Hestia.Access/Requests/Product/Queries/GetExisting/GetExistingProductByExternalIdQuery.cs +++ b/Hestia.Access/Requests/Product/Queries/GetExisting/GetExistingProductByExternalIdQuery.cs @@ -1,4 +1,4 @@ -using MediatR; +using Hestia.Mediator.Infrastructure.Messaging; namespace Hestia.Access.Requests.Product.Queries.GetExisting; diff --git a/Hestia.Access/Requests/Product/Queries/GetExisting/GetExistingProductByIdQuery.cs b/Hestia.Access/Requests/Product/Queries/GetExisting/GetExistingProductByIdQuery.cs index ea18012..5552ba7 100644 --- a/Hestia.Access/Requests/Product/Queries/GetExisting/GetExistingProductByIdQuery.cs +++ b/Hestia.Access/Requests/Product/Queries/GetExisting/GetExistingProductByIdQuery.cs @@ -1,4 +1,4 @@ -using MediatR; +using Hestia.Mediator.Infrastructure.Messaging; namespace Hestia.Access.Requests.Product.Queries.GetExisting; diff --git a/Hestia.Access/Requests/Product/Queries/GetExisting/GetExistingProductByUserQuery.cs b/Hestia.Access/Requests/Product/Queries/GetExisting/GetExistingProductByUserQuery.cs index 235211c..70abde2 100644 --- a/Hestia.Access/Requests/Product/Queries/GetExisting/GetExistingProductByUserQuery.cs +++ b/Hestia.Access/Requests/Product/Queries/GetExisting/GetExistingProductByUserQuery.cs @@ -1,4 +1,4 @@ -using MediatR; +using Hestia.Mediator.Infrastructure.Messaging; namespace Hestia.Access.Requests.Product.Queries.GetExisting; diff --git a/Hestia.Access/Requests/Shared/ExecuteSaveChangesAsync.cs b/Hestia.Access/Requests/Shared/ExecuteSaveChangesAsync.cs index c98eb63..67b2463 100644 --- a/Hestia.Access/Requests/Shared/ExecuteSaveChangesAsync.cs +++ b/Hestia.Access/Requests/Shared/ExecuteSaveChangesAsync.cs @@ -1,4 +1,5 @@ -using MediatR; +using Hestia.Mediator.Infrastructure.Messaging; +using Hestia.Mediator.Infrastructure.Types; namespace Hestia.Access.Requests.Shared; diff --git a/Hestia.Access/Requests/User/Commands/CreateUser/CreateApplicationUserCommand.cs b/Hestia.Access/Requests/User/Commands/CreateUser/CreateApplicationUserCommand.cs index 798632c..2d9b309 100644 --- a/Hestia.Access/Requests/User/Commands/CreateUser/CreateApplicationUserCommand.cs +++ b/Hestia.Access/Requests/User/Commands/CreateUser/CreateApplicationUserCommand.cs @@ -1,5 +1,5 @@ using Hestia.Domain.Models.Authentication; -using MediatR; +using Hestia.Mediator.Infrastructure.Messaging; using Microsoft.AspNetCore.Identity; namespace Hestia.Access.Requests.User.Commands.CreateUser; diff --git a/Hestia.Access/Requests/User/Commands/CreateUser/CreateInternalUserCommand.cs b/Hestia.Access/Requests/User/Commands/CreateUser/CreateInternalUserCommand.cs index 46b5d53..42759e4 100644 --- a/Hestia.Access/Requests/User/Commands/CreateUser/CreateInternalUserCommand.cs +++ b/Hestia.Access/Requests/User/Commands/CreateUser/CreateInternalUserCommand.cs @@ -1,4 +1,4 @@ -using MediatR; +using Hestia.Mediator.Infrastructure.Messaging; namespace Hestia.Access.Requests.User.Commands.CreateUser; diff --git a/Hestia.Access/Requests/User/Queries/UserExists/GetExistingUserQuery.cs b/Hestia.Access/Requests/User/Queries/UserExists/GetExistingUserQuery.cs index 767672e..08d743c 100644 --- a/Hestia.Access/Requests/User/Queries/UserExists/GetExistingUserQuery.cs +++ b/Hestia.Access/Requests/User/Queries/UserExists/GetExistingUserQuery.cs @@ -1,5 +1,5 @@ using Hestia.Domain.Models.Authentication; -using MediatR; +using Hestia.Mediator.Infrastructure.Messaging; namespace Hestia.Access.Requests.User.Queries.UserExists; diff --git a/Hestia.Access/Requests/User/Queries/ValidateUserLogin/ValidateUserLoginQuery.cs b/Hestia.Access/Requests/User/Queries/ValidateUserLogin/ValidateUserLoginQuery.cs index 46e0c5d..1d1fab1 100644 --- a/Hestia.Access/Requests/User/Queries/ValidateUserLogin/ValidateUserLoginQuery.cs +++ b/Hestia.Access/Requests/User/Queries/ValidateUserLogin/ValidateUserLoginQuery.cs @@ -1,5 +1,5 @@ using Hestia.Domain.Models.Authentication; -using MediatR; +using Hestia.Mediator.Infrastructure.Messaging; namespace Hestia.Access.Requests.User.Queries.ValidateUserLogin; diff --git a/Hestia.Api.Tests/Configuration/Application/ConfigureServicesTests.cs b/Hestia.Api.Tests/Configuration/Application/ConfigureServicesTests.cs index bef9080..2a1e710 100644 --- a/Hestia.Api.Tests/Configuration/Application/ConfigureServicesTests.cs +++ b/Hestia.Api.Tests/Configuration/Application/ConfigureServicesTests.cs @@ -2,17 +2,18 @@ using Hestia.Api.Configuration.Application; using Hestia.Api.Tests.Shared; using Hestia.Application.Interfaces.Authentication; -using Hestia.Application.Interfaces.Infrastructure; using Hestia.Application.Interfaces.Product; using Hestia.Application.Interfaces.Response; using Hestia.Domain.Models.Authentication; +using Hestia.Mediator.Infrastructure; +using Hestia.Mediator.Infrastructure.Layers; using Hestia.Persistence.Contexts; -using MediatR; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.AspNetCore.Mvc.Formatters; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Options; @@ -33,7 +34,10 @@ public void ConfigureApplicationServices_ShouldRegisterExpectedServices() services.AddSingleton(new MockWebHostEnvironment { EnvironmentName = Environments.Development }); services.AddMemoryCache(); services.AddDataProtection(); - services.AddScoped(); + services.AddDbContext(options => + { + options.UseInMemoryDatabase("TestDb"); + }); // Add Identity services services.AddIdentityCore(options => { }) @@ -67,10 +71,6 @@ public void ConfigureApplicationServices_ShouldRegisterExpectedServices() var mediator = serviceProvider.GetService(); Assert.NotNull(mediator); - // Check if AutoMapper is registered - var mapper = serviceProvider.GetService(); - Assert.NotNull(mapper); - // Check if FluentValidation validators are registered var validators = serviceProvider.GetServices(); Assert.NotNull(validators); diff --git a/Hestia.Api.Tests/Configuration/Extensions/WebApplicationBuilderExtensionsTests.cs b/Hestia.Api.Tests/Configuration/Extensions/WebApplicationBuilderExtensionsTests.cs index 3845dec..cef50b7 100644 --- a/Hestia.Api.Tests/Configuration/Extensions/WebApplicationBuilderExtensionsTests.cs +++ b/Hestia.Api.Tests/Configuration/Extensions/WebApplicationBuilderExtensionsTests.cs @@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Identity; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.IdentityModel.Tokens; @@ -35,7 +36,10 @@ public void ConfigureAuthentication_ShouldAddAuthenticationServices() { // Arrange var builder = WebApplication.CreateBuilder(); - builder.Services.AddScoped(); + builder.Services.AddDbContext(options => + { + options.UseInMemoryDatabase("TestDb"); + }); builder.Environment.EnvironmentName = "Development"; builder.Configuration.AddJsonFile("appsettings.json"); builder.Configuration.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json"); diff --git a/Hestia.Api.Tests/Configuration/Infrastructure/ConfigureServicesTests.cs b/Hestia.Api.Tests/Configuration/Infrastructure/ConfigureServicesTests.cs index bcd24c8..20070e9 100644 --- a/Hestia.Api.Tests/Configuration/Infrastructure/ConfigureServicesTests.cs +++ b/Hestia.Api.Tests/Configuration/Infrastructure/ConfigureServicesTests.cs @@ -2,6 +2,7 @@ using Hestia.Background.Interfaces; using Hestia.Background.Tasks; using Hestia.Persistence.Contexts; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -47,8 +48,15 @@ public static class ServiceCollectionExtensions { public static IServiceCollection ConfigureInfrastructuresServices(this IServiceCollection services) { - services.AddScoped(); - services.AddScoped(); + services.AddDbContext(options => + { + options.UseInMemoryDatabase("TestDb"); + }); + + services.AddDbContext(options => + { + options.UseInMemoryDatabase("TestDb"); + }); services.AddMemoryCache(); services.ConfigureBackgroundRunners(); diff --git a/Hestia.Api.Tests/Hestia.Api.Tests.csproj b/Hestia.Api.Tests/Hestia.Api.Tests.csproj index 5d0e27d..c815126 100644 --- a/Hestia.Api.Tests/Hestia.Api.Tests.csproj +++ b/Hestia.Api.Tests/Hestia.Api.Tests.csproj @@ -8,13 +8,20 @@ - - - - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + - - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/Hestia.Api/Configuration/Application/ConfigureServices.cs b/Hestia.Api/Configuration/Application/ConfigureServices.cs index bccb9e3..ca8cda6 100644 --- a/Hestia.Api/Configuration/Application/ConfigureServices.cs +++ b/Hestia.Api/Configuration/Application/ConfigureServices.cs @@ -1,13 +1,16 @@ using FluentValidation; using Hestia.Application.Handlers.Validation; using Hestia.Application.Interfaces.Authentication; -using Hestia.Application.Interfaces.Infrastructure; using Hestia.Application.Interfaces.Product; using Hestia.Application.Interfaces.Response; using Hestia.Application.Services; using Hestia.Application.Services.Authentication; using Hestia.Application.Services.Product; -using MediatR; +using Hestia.Mediator.Infrastructure; +using Hestia.Mediator.Infrastructure.Layers; +using Hestia.Mediator.Infrastructure.Messaging; +using Hestia.Mediator.Infrastructure.Pipeline; +using Hestia.Mediator.Services; using System.Reflection; using System.Text.Json.Serialization; @@ -22,19 +25,12 @@ public static IServiceCollection ConfigureApplicationServices(this IServiceColle services.AddApiServices(); services.AddApplicationServices(); - // MediatR - services.AddMediatR(); - - // AutoMapper - services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies()); + // Mediator pattern + services.AddMediator(); // FluentValidation validators services.AddFluentValidationValidators(); - // MediatR layers - services.AddTransient(); - services.AddTransient(); - services.AddControllers().AddJsonOptions(options => { options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()); @@ -43,16 +39,49 @@ public static IServiceCollection ConfigureApplicationServices(this IServiceColle return services; } - public static IServiceCollection AddMediatR(this IServiceCollection services) + public static IServiceCollection AddMediator(this IServiceCollection services) { - services.AddMediatR(cfg => - { - cfg.RegisterServicesFromAssemblies(AppDomain.CurrentDomain.GetAssemblies()); - }); + // Mediator pattern service + services.AddSingleton(); + + // Mediator pattern layers + services.AddTransient(); + services.AddTransient(); + + services.AddRequestHandlers(); return services; } + private static void AddRequestHandlers(this IServiceCollection services) + { + var handlerInterfaceType = typeof(IRequestHandler<,>); + + var assemblies = Directory + .GetFiles(AppContext.BaseDirectory, "*.dll", SearchOption.TopDirectoryOnly) + .Where(file => + { + string name = Path.GetFileName(file); + // Adjust filter to match your solution's assemblies + return name.StartsWith("Hestia.", StringComparison.OrdinalIgnoreCase); + }) + .Select(Assembly.LoadFrom) + .ToList(); + + var handlerTypes = assemblies + .SelectMany(a => a.GetTypes()) + .Where(t => !t.IsAbstract && !t.IsInterface) + .SelectMany(t => t.GetInterfaces(), (type, iface) => new { type, iface }) + .Where(x => x.iface.IsGenericType && + x.iface.GetGenericTypeDefinition() == handlerInterfaceType) + .ToList(); + + foreach (var handler in handlerTypes) + { + services.AddTransient(handler.iface, handler.type); + } + } + public static IServiceCollection AddFluentValidationValidators(this IServiceCollection services) { services.AddValidatorsFromAssembly(Assembly.Load("Hestia.Domain")); diff --git a/Hestia.Api/Configuration/Infrastructure/ConfigureServices.cs b/Hestia.Api/Configuration/Infrastructure/ConfigureServices.cs index 363060d..8f06651 100644 --- a/Hestia.Api/Configuration/Infrastructure/ConfigureServices.cs +++ b/Hestia.Api/Configuration/Infrastructure/ConfigureServices.cs @@ -2,6 +2,7 @@ using Hestia.Background.Interfaces; using Hestia.Background.Tasks; using Hestia.Persistence.Contexts; +using Microsoft.EntityFrameworkCore; namespace Hestia.Api.Configuration.Infrastructure; @@ -27,8 +28,17 @@ private static IServiceCollection ConfigureBackgroundRunners(this IServiceCollec private static IServiceCollection ConfigureDbContexts(this IServiceCollection services) { - services.AddScoped(); - services.AddScoped(); + services.AddDbContext((serviceProvider, options) => + { + var configuration = serviceProvider.GetRequiredService(); + options.UseNpgsql(configuration.GetConnectionString("AuthServer")); + }); + + services.AddDbContext((serviceProvider, options) => + { + var configuration = serviceProvider.GetRequiredService(); + options.UseNpgsql(configuration.GetConnectionString("DataServer")); + }); return services; } diff --git a/Hestia.Api/Controllers/AuthenticationController.cs b/Hestia.Api/Controllers/AuthenticationController.cs index 8412077..bbaf7d3 100644 --- a/Hestia.Api/Controllers/AuthenticationController.cs +++ b/Hestia.Api/Controllers/AuthenticationController.cs @@ -1,9 +1,9 @@ using Hestia.Application.Handlers.Authentication.Commands.Login; using Hestia.Application.Handlers.Authentication.Commands.Register; -using Hestia.Application.Interfaces.Infrastructure; using Hestia.Application.Models.Authentication.Inbound; using Hestia.Application.Models.Authentication.Outbound; using Hestia.Application.Models.Shared; +using Hestia.Mediator.Infrastructure.Layers; using Microsoft.AspNetCore.Mvc; namespace Hestia.Api.Controllers; diff --git a/Hestia.Api/Controllers/ProductController.cs b/Hestia.Api/Controllers/ProductController.cs index ee72733..3e659c9 100644 --- a/Hestia.Api/Controllers/ProductController.cs +++ b/Hestia.Api/Controllers/ProductController.cs @@ -2,11 +2,11 @@ using Hestia.Application.Handlers.Product.Commands.DeleteProduct; using Hestia.Application.Handlers.Product.Commands.UpdateProduct; using Hestia.Application.Handlers.Product.Queries.GetProduct; -using Hestia.Application.Interfaces.Infrastructure; using Hestia.Application.Models.Shared; using Hestia.Domain.Models.Product.Inbound.CreateProduct; using Hestia.Domain.Models.Product.Inbound.GetProduct; using Hestia.Domain.Models.Product.Inbound.UpdateProduct; +using Hestia.Mediator.Infrastructure.Layers; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; diff --git a/Hestia.Api/Hestia.Api.csproj b/Hestia.Api/Hestia.Api.csproj index d2bf889..88377bf 100644 --- a/Hestia.Api/Hestia.Api.csproj +++ b/Hestia.Api/Hestia.Api.csproj @@ -14,25 +14,27 @@ - - - - - - + + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + - - + + + + diff --git a/Hestia.Application/Models/Authentication/Inbound/ApplicationUserLoginDto.cs b/Hestia.Application.Models/Authentication/Inbound/ApplicationUserLoginDto.cs similarity index 100% rename from Hestia.Application/Models/Authentication/Inbound/ApplicationUserLoginDto.cs rename to Hestia.Application.Models/Authentication/Inbound/ApplicationUserLoginDto.cs diff --git a/Hestia.Application/Models/Authentication/Inbound/ApplicationUserRegisterDto.cs b/Hestia.Application.Models/Authentication/Inbound/ApplicationUserRegisterDto.cs similarity index 100% rename from Hestia.Application/Models/Authentication/Inbound/ApplicationUserRegisterDto.cs rename to Hestia.Application.Models/Authentication/Inbound/ApplicationUserRegisterDto.cs diff --git a/Hestia.Application/Models/Authentication/Inbound/ApplicationUserRegisterDtoValidator.cs b/Hestia.Application.Models/Authentication/Inbound/ApplicationUserRegisterDtoValidator.cs similarity index 100% rename from Hestia.Application/Models/Authentication/Inbound/ApplicationUserRegisterDtoValidator.cs rename to Hestia.Application.Models/Authentication/Inbound/ApplicationUserRegisterDtoValidator.cs diff --git a/Hestia.Application/Models/Authentication/Outbound/ApplicationUserLoginResponseDto.cs b/Hestia.Application.Models/Authentication/Outbound/ApplicationUserLoginResponseDto.cs similarity index 100% rename from Hestia.Application/Models/Authentication/Outbound/ApplicationUserLoginResponseDto.cs rename to Hestia.Application.Models/Authentication/Outbound/ApplicationUserLoginResponseDto.cs diff --git a/Hestia.Application/Models/Authentication/Outbound/ApplicationUserRegisterResponseDto.cs b/Hestia.Application.Models/Authentication/Outbound/ApplicationUserRegisterResponseDto.cs similarity index 100% rename from Hestia.Application/Models/Authentication/Outbound/ApplicationUserRegisterResponseDto.cs rename to Hestia.Application.Models/Authentication/Outbound/ApplicationUserRegisterResponseDto.cs diff --git a/Hestia.Application.Models/Hestia.Application.Models.csproj b/Hestia.Application.Models/Hestia.Application.Models.csproj new file mode 100644 index 0000000..31bf826 --- /dev/null +++ b/Hestia.Application.Models/Hestia.Application.Models.csproj @@ -0,0 +1,17 @@ + + + + net9.0 + enable + enable + + + + + + + + + + + diff --git a/Hestia.Application/Models/Shared/ApiResponse.cs b/Hestia.Application.Models/Shared/ApiResponse.cs similarity index 100% rename from Hestia.Application/Models/Shared/ApiResponse.cs rename to Hestia.Application.Models/Shared/ApiResponse.cs diff --git a/Hestia.Application/Models/Shared/AppSettings.cs b/Hestia.Application.Models/Shared/AppSettings.cs similarity index 100% rename from Hestia.Application/Models/Shared/AppSettings.cs rename to Hestia.Application.Models/Shared/AppSettings.cs diff --git a/Hestia.Application.Tests/Hestia.Application.Tests.csproj b/Hestia.Application.Tests/Hestia.Application.Tests.csproj index ea778b7..a50c58b 100644 --- a/Hestia.Application.Tests/Hestia.Application.Tests.csproj +++ b/Hestia.Application.Tests/Hestia.Application.Tests.csproj @@ -8,16 +8,23 @@ - - - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + - - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + diff --git a/Hestia.Application.Tests/Profiles/Authentication/ApplicationUserProfileTests.cs b/Hestia.Application.Tests/Profiles/Authentication/ApplicationUserProfileTests.cs index 680c8f1..55a6e29 100644 --- a/Hestia.Application.Tests/Profiles/Authentication/ApplicationUserProfileTests.cs +++ b/Hestia.Application.Tests/Profiles/Authentication/ApplicationUserProfileTests.cs @@ -1,6 +1,4 @@ -using AutoMapper; -using Hestia.Application.Models.Authentication.Outbound; -using Hestia.Application.Profiles.Authentication; +using Hestia.Application.Mappers; using Hestia.Domain.Enumerations; using Hestia.Domain.Models.Authentication; @@ -8,28 +6,6 @@ namespace Hestia.Application.Tests.Profiles.Authentication; public class ApplicationUserProfileTests { - private readonly IMapper _mapper; - - public ApplicationUserProfileTests() - { - var config = new MapperConfiguration(cfg => - { - cfg.AddProfile(); - }); - _mapper = config.CreateMapper(); - } - - [Fact] - public void ApplicationUserProfile_ConfigurationIsValid() - { - var config = new MapperConfiguration(cfg => - { - cfg.AddProfile(); - }); - - config.AssertConfigurationIsValid(); - } - [Fact] public void ApplicationUser_To_ApplicationUserRegisterResponseDto_Mapping() { @@ -43,30 +19,10 @@ public void ApplicationUser_To_ApplicationUserRegisterResponseDto_Mapping() }; // Act - var registerResponseDto = _mapper.Map(applicationUser); + var registerResponseDto = applicationUser.ToRegisterResponseDto(); // Assert Assert.Equal(applicationUser.UserName, registerResponseDto.Username); Assert.Equal(applicationUser.Email, registerResponseDto.Email); } - - [Fact] - public void ApplicationUser_To_ApplicationUserLoginResponseDto_Mapping() - { - // Arrange - var applicationUser = new ApplicationUser - { - Id = "1", - UserName = "testuser", - Email = "test@example.com", - Role = Role.User - }; - - // Act - var loginResponseDto = _mapper.Map(applicationUser); - - // Assert - Assert.Equal(applicationUser.UserName, loginResponseDto.Username); - Assert.Equal(applicationUser.Email, loginResponseDto.Email); - } } \ No newline at end of file diff --git a/Hestia.Application.Tests/Profiles/Product/ProductProfileTests.cs b/Hestia.Application.Tests/Profiles/Product/ProductProfileTests.cs index ada6619..3b2b3dc 100644 --- a/Hestia.Application.Tests/Profiles/Product/ProductProfileTests.cs +++ b/Hestia.Application.Tests/Profiles/Product/ProductProfileTests.cs @@ -1,34 +1,10 @@ -using AutoMapper; -using Hestia.Application.Profiles.Product; -using Hestia.Domain.Models.Product.Inbound.GetProduct; +using Hestia.Application.Mappers; using Hestia.Domain.Models.Product.Inbound.UpdateProduct; namespace Hestia.Application.Tests.Profiles.Product; public class ProductProfileTests { - private readonly IMapper _mapper; - - public ProductProfileTests() - { - var config = new MapperConfiguration(cfg => - { - cfg.AddProfile(); - }); - _mapper = config.CreateMapper(); - } - - [Fact] - public void ProductProfile_ConfigurationIsValid() - { - var config = new MapperConfiguration(cfg => - { - cfg.AddProfile(); - }); - - config.AssertConfigurationIsValid(); - } - [Fact] public void UpdateProductDto_To_Product_Mapping() { @@ -41,26 +17,27 @@ public void UpdateProductDto_To_Product_Mapping() Description = "UpdatedDescription", Price = 99.99M }; - var product = new Access.Entities.Product.Product(Guid.NewGuid()) + + var product = new Access.Entities.Product.Product(updateProductDto.Id) { - ExternalId = "originalExternalId", + ExternalId = updateProductDto.ExternalId, DateCreated = DateTime.UtcNow.AddDays(-1), - Name = "UpdatedName", - Description = "UpdatedDescription", - Price = 99.99M, + Name = "OriginalName", + Description = "OriginalDescription", + Price = 10.00M, UserId = "UserId" }; // Act - var mappedProduct = _mapper.Map(updateProductDto, product); + product.ApplyUpdate(updateProductDto); // Assert - Assert.Equal(product.Id, mappedProduct.Id); - Assert.Equal(product.ExternalId, mappedProduct.ExternalId); - Assert.Equal(product.DateCreated, mappedProduct.DateCreated); - Assert.Equal(updateProductDto.Name, mappedProduct.Name); - Assert.Equal(updateProductDto.Description, mappedProduct.Description); - Assert.Equal(updateProductDto.Price, mappedProduct.Price); + Assert.Equal(updateProductDto.Id, product.Id); + Assert.Equal(updateProductDto.ExternalId, product.ExternalId); + Assert.Equal(updateProductDto.Name, product.Name); + Assert.Equal(updateProductDto.Description, product.Description); + Assert.Equal(updateProductDto.Price, product.Price); + Assert.True(product.DateEdited > product.DateCreated); } [Fact] @@ -71,7 +48,7 @@ public void Product_To_GetProductResponseDto_Mapping() { ExternalId = "originalExternalId", DateCreated = DateTime.UtcNow.AddDays(-1), - DateEdited = DateTime.UtcNow.AddDays(-1), + DateEdited = DateTime.UtcNow, Name = "UpdatedName", Description = "UpdatedDescription", Price = 99.99M, @@ -79,13 +56,15 @@ public void Product_To_GetProductResponseDto_Mapping() }; // Act - var productResponseDto = _mapper.Map(product); + var productResponseDto = product.ToResponseDto(); // Assert Assert.Equal(product.Id, productResponseDto.Id); Assert.Equal(product.ExternalId, productResponseDto.ExternalId); + Assert.Equal(product.DateCreated, productResponseDto.DateCreated); + Assert.Equal(product.DateEdited, productResponseDto.DateEdited); Assert.Equal(product.Name, productResponseDto.Name); Assert.Equal(product.Description, productResponseDto.Description); Assert.Equal(product.Price, productResponseDto.Price); } -} \ No newline at end of file +} diff --git a/Hestia.Application.Tests/Services/Authentication/AuthServiceTests.cs b/Hestia.Application.Tests/Services/Authentication/AuthServiceTests.cs index a94fb25..cadc59a 100644 --- a/Hestia.Application.Tests/Services/Authentication/AuthServiceTests.cs +++ b/Hestia.Application.Tests/Services/Authentication/AuthServiceTests.cs @@ -1,12 +1,10 @@ -using AutoMapper; -using Hestia.Access.Requests.User.Queries.UserExists; +using Hestia.Access.Requests.User.Queries.UserExists; using Hestia.Access.Requests.User.Queries.ValidateUserLogin; using Hestia.Application.Interfaces.Authentication; -using Hestia.Application.Interfaces.Infrastructure; using Hestia.Application.Models.Authentication.Inbound; -using Hestia.Application.Models.Authentication.Outbound; using Hestia.Application.Services.Authentication; using Hestia.Domain.Models.Authentication; +using Hestia.Mediator.Infrastructure.Layers; using Microsoft.Extensions.Logging; using Moq; using System.Net; @@ -17,7 +15,6 @@ public class AuthServiceTests { private readonly Mock _mockTokenService; private readonly Mock _mockUserService; - private readonly Mock _mockMapper; private readonly Mock _mockAccessLayer; private readonly Mock> _mockLogger; private readonly AuthService _authService; @@ -26,13 +23,11 @@ public AuthServiceTests() { _mockTokenService = new Mock(); _mockUserService = new Mock(); - _mockMapper = new Mock(); _mockAccessLayer = new Mock(); _mockLogger = new Mock>(); _authService = new AuthService( _mockTokenService.Object, _mockUserService.Object, - _mockMapper.Object, _mockAccessLayer.Object, _mockLogger.Object ); @@ -111,21 +106,18 @@ public async Task RegisterAsync_ShouldReturnCreated_WhenUserIsRegisteredSuccessf var newUser = new ApplicationUser { UserName = "newuser", Email = "newuser@example.com" }; _mockAccessLayer.Setup(x => x.ExecuteAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync((ApplicationUser)null); + .ReturnsAsync(null as ApplicationUser); _mockUserService.Setup(x => x.CreateUserAsync(model, It.IsAny())) .ReturnsAsync(newUser); - _mockMapper.Setup(m => m.Map(It.IsAny())) - .Returns(new ApplicationUserRegisterResponseDto { Username = "newuser", Email = "newuser@example.com" }); - // Act var (response, statusCode) = await _authService.RegisterAsync(model); // Assert Assert.Equal(HttpStatusCode.Created, statusCode); - Assert.Equal("newuser", response.Username); - Assert.Equal("newuser@example.com", response.Email); + Assert.Equal("newuser", response?.Username); + Assert.Equal("newuser@example.com", response?.Email); } [Fact] diff --git a/Hestia.Application.Tests/Services/Authentication/TokenServiceTests.cs b/Hestia.Application.Tests/Services/Authentication/TokenServiceTests.cs index a67eef1..f79a8ec 100644 --- a/Hestia.Application.Tests/Services/Authentication/TokenServiceTests.cs +++ b/Hestia.Application.Tests/Services/Authentication/TokenServiceTests.cs @@ -1,10 +1,10 @@ using Hestia.Access.Requests.Authentication.Commands.CreateTokenLog; -using Hestia.Application.Interfaces.Infrastructure; using Hestia.Application.Models.Shared; using Hestia.Application.Services.Authentication; using Hestia.Domain.Enumerations; using Hestia.Domain.Models.Authentication; -using MediatR; +using Hestia.Mediator.Infrastructure.Layers; +using Hestia.Mediator.Infrastructure.Types; using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Options; diff --git a/Hestia.Application.Tests/Services/Authentication/UserServiceTests.cs b/Hestia.Application.Tests/Services/Authentication/UserServiceTests.cs index 8abe83b..5171091 100644 --- a/Hestia.Application.Tests/Services/Authentication/UserServiceTests.cs +++ b/Hestia.Application.Tests/Services/Authentication/UserServiceTests.cs @@ -1,9 +1,9 @@ using Hestia.Access.Requests.User.Commands.CreateUser; -using Hestia.Application.Interfaces.Infrastructure; using Hestia.Application.Models.Authentication.Inbound; using Hestia.Application.Services.Authentication; using Hestia.Domain.Enumerations; using Hestia.Domain.Models.Authentication; +using Hestia.Mediator.Infrastructure.Layers; using Microsoft.AspNetCore.Identity; using Moq; diff --git a/Hestia.Application.Tests/Services/Product/ProductServiceTests.cs b/Hestia.Application.Tests/Services/Product/ProductServiceTests.cs index d7d2ed4..ec2fbd9 100644 --- a/Hestia.Application.Tests/Services/Product/ProductServiceTests.cs +++ b/Hestia.Application.Tests/Services/Product/ProductServiceTests.cs @@ -1,13 +1,12 @@ -using AutoMapper; -using Hestia.Access.Requests.Product.Commands.CreateProduct; +using Hestia.Access.Requests.Product.Commands.CreateProduct; using Hestia.Access.Requests.Product.Commands.DeleteProduct; using Hestia.Access.Requests.Product.Commands.UpdateProduct; using Hestia.Access.Requests.Product.Queries.GetExisting; -using Hestia.Application.Interfaces.Infrastructure; using Hestia.Application.Services.Product; using Hestia.Domain.Models.Product.Inbound.CreateProduct; using Hestia.Domain.Models.Product.Inbound.GetProduct; using Hestia.Domain.Models.Product.Inbound.UpdateProduct; +using Hestia.Mediator.Infrastructure.Layers; using Microsoft.Extensions.Logging; using Moq; using System.Net; @@ -17,16 +16,14 @@ namespace Hestia.Application.Tests.Services.Product; public class ProductServiceCreateProductTests { private readonly Mock _mockAccessLayer; - private readonly Mock _mockMapper; private readonly Mock> _mockLogger; private readonly ProductService _productService; public ProductServiceCreateProductTests() { _mockAccessLayer = new Mock(); - _mockMapper = new Mock(); _mockLogger = new Mock>(); - _productService = new ProductService(_mockAccessLayer.Object, _mockMapper.Object, _mockLogger.Object); + _productService = new ProductService(_mockAccessLayer.Object, _mockLogger.Object); } [Fact] @@ -139,8 +136,6 @@ public async Task UpdateProductAsync_ShouldReturnOk_WhenProductIsUpdatedSuccessf _mockAccessLayer.Setup(x => x.ExecuteAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(true); - _mockMapper.Setup(m => m.Map(updateProductDto, existingProduct)).Returns(existingProduct); - // Act var (isUpdated, statusCode) = await _productService.UpdateProductAsync(updateProductDto); @@ -304,8 +299,6 @@ public async Task GetProductAsync_ShouldReturnOk_WhenProductIsFound() _mockAccessLayer.Setup(x => x.ExecuteAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(existingProduct); - _mockMapper.Setup(m => m.Map(existingProduct)).Returns(getProductResponseDto); - // Act var (productResponse, statusCode) = await _productService.GetProductAsync(getProductDto); diff --git a/Hestia.Application/Handlers/Authentication/Commands/Login/ApplicationUserLoginCommand.cs b/Hestia.Application/Handlers/Authentication/Commands/Login/ApplicationUserLoginCommand.cs index 66f1610..2ea67c3 100644 --- a/Hestia.Application/Handlers/Authentication/Commands/Login/ApplicationUserLoginCommand.cs +++ b/Hestia.Application/Handlers/Authentication/Commands/Login/ApplicationUserLoginCommand.cs @@ -1,7 +1,7 @@ using Hestia.Application.Models.Authentication.Inbound; using Hestia.Application.Models.Authentication.Outbound; using Hestia.Application.Models.Shared; -using MediatR; +using Hestia.Mediator.Infrastructure.Messaging; namespace Hestia.Application.Handlers.Authentication.Commands.Login; diff --git a/Hestia.Application/Handlers/Authentication/Commands/Login/ApplicationUserLoginCommandHandler.cs b/Hestia.Application/Handlers/Authentication/Commands/Login/ApplicationUserLoginCommandHandler.cs index 3d6595f..1944945 100644 --- a/Hestia.Application/Handlers/Authentication/Commands/Login/ApplicationUserLoginCommandHandler.cs +++ b/Hestia.Application/Handlers/Authentication/Commands/Login/ApplicationUserLoginCommandHandler.cs @@ -1,7 +1,7 @@ using Hestia.Application.Interfaces.Authentication; using Hestia.Application.Models.Authentication.Outbound; using Hestia.Application.Models.Shared; -using MediatR; +using Hestia.Mediator.Infrastructure.Messaging; using System.Net; namespace Hestia.Application.Handlers.Authentication.Commands.Login; diff --git a/Hestia.Application/Handlers/Authentication/Commands/Register/ApplicationUserRegisterCommand.cs b/Hestia.Application/Handlers/Authentication/Commands/Register/ApplicationUserRegisterCommand.cs index d4b1ac6..135c333 100644 --- a/Hestia.Application/Handlers/Authentication/Commands/Register/ApplicationUserRegisterCommand.cs +++ b/Hestia.Application/Handlers/Authentication/Commands/Register/ApplicationUserRegisterCommand.cs @@ -1,7 +1,7 @@ using Hestia.Application.Models.Authentication.Inbound; using Hestia.Application.Models.Authentication.Outbound; using Hestia.Application.Models.Shared; -using MediatR; +using Hestia.Mediator.Infrastructure.Messaging; namespace Hestia.Application.Handlers.Authentication.Commands.Register; diff --git a/Hestia.Application/Handlers/Authentication/Commands/Register/ApplicationUserRegisterCommandHandler.cs b/Hestia.Application/Handlers/Authentication/Commands/Register/ApplicationUserRegisterCommandHandler.cs index c172152..5cde267 100644 --- a/Hestia.Application/Handlers/Authentication/Commands/Register/ApplicationUserRegisterCommandHandler.cs +++ b/Hestia.Application/Handlers/Authentication/Commands/Register/ApplicationUserRegisterCommandHandler.cs @@ -1,7 +1,7 @@ using Hestia.Application.Interfaces.Authentication; using Hestia.Application.Models.Authentication.Outbound; using Hestia.Application.Models.Shared; -using MediatR; +using Hestia.Mediator.Infrastructure.Messaging; using System.Net; namespace Hestia.Application.Handlers.Authentication.Commands.Register; diff --git a/Hestia.Application/Handlers/Product/Commands/CreateProduct/CreateProductCommand.cs b/Hestia.Application/Handlers/Product/Commands/CreateProduct/CreateProductCommand.cs index 74725c2..5dfd001 100644 --- a/Hestia.Application/Handlers/Product/Commands/CreateProduct/CreateProductCommand.cs +++ b/Hestia.Application/Handlers/Product/Commands/CreateProduct/CreateProductCommand.cs @@ -1,6 +1,6 @@ using Hestia.Application.Models.Shared; using Hestia.Domain.Models.Product.Inbound.CreateProduct; -using MediatR; +using Hestia.Mediator.Infrastructure.Messaging; namespace Hestia.Application.Handlers.Product.Commands.CreateProduct; diff --git a/Hestia.Application/Handlers/Product/Commands/CreateProduct/CreateProductCommandHandler.cs b/Hestia.Application/Handlers/Product/Commands/CreateProduct/CreateProductCommandHandler.cs index 3935d79..9764fac 100644 --- a/Hestia.Application/Handlers/Product/Commands/CreateProduct/CreateProductCommandHandler.cs +++ b/Hestia.Application/Handlers/Product/Commands/CreateProduct/CreateProductCommandHandler.cs @@ -1,7 +1,7 @@ using Hestia.Application.Interfaces.Authentication; using Hestia.Application.Interfaces.Product; using Hestia.Application.Models.Shared; -using MediatR; +using Hestia.Mediator.Infrastructure.Messaging; using System.Net; namespace Hestia.Application.Handlers.Product.Commands.CreateProduct; diff --git a/Hestia.Application/Handlers/Product/Commands/DeleteProduct/DeleteProductCommand.cs b/Hestia.Application/Handlers/Product/Commands/DeleteProduct/DeleteProductCommand.cs index a4afe89..02a0414 100644 --- a/Hestia.Application/Handlers/Product/Commands/DeleteProduct/DeleteProductCommand.cs +++ b/Hestia.Application/Handlers/Product/Commands/DeleteProduct/DeleteProductCommand.cs @@ -1,6 +1,6 @@ using Hestia.Application.Models.Shared; using Hestia.Domain.Models.Product.Inbound.GetProduct; -using MediatR; +using Hestia.Mediator.Infrastructure.Messaging; namespace Hestia.Application.Handlers.Product.Commands.DeleteProduct; diff --git a/Hestia.Application/Handlers/Product/Commands/DeleteProduct/DeleteProductCommandHandler.cs b/Hestia.Application/Handlers/Product/Commands/DeleteProduct/DeleteProductCommandHandler.cs index 7927a1b..407b963 100644 --- a/Hestia.Application/Handlers/Product/Commands/DeleteProduct/DeleteProductCommandHandler.cs +++ b/Hestia.Application/Handlers/Product/Commands/DeleteProduct/DeleteProductCommandHandler.cs @@ -1,6 +1,6 @@ using Hestia.Application.Interfaces.Product; using Hestia.Application.Models.Shared; -using MediatR; +using Hestia.Mediator.Infrastructure.Messaging; using System.Net; namespace Hestia.Application.Handlers.Product.Commands.DeleteProduct; diff --git a/Hestia.Application/Handlers/Product/Commands/UpdateProduct/UpdateProductCommand.cs b/Hestia.Application/Handlers/Product/Commands/UpdateProduct/UpdateProductCommand.cs index 92f0eb2..1c4f0a0 100644 --- a/Hestia.Application/Handlers/Product/Commands/UpdateProduct/UpdateProductCommand.cs +++ b/Hestia.Application/Handlers/Product/Commands/UpdateProduct/UpdateProductCommand.cs @@ -1,6 +1,6 @@ using Hestia.Application.Models.Shared; using Hestia.Domain.Models.Product.Inbound.UpdateProduct; -using MediatR; +using Hestia.Mediator.Infrastructure.Messaging; namespace Hestia.Application.Handlers.Product.Commands.UpdateProduct; diff --git a/Hestia.Application/Handlers/Product/Commands/UpdateProduct/UpdateProductCommandHandler.cs b/Hestia.Application/Handlers/Product/Commands/UpdateProduct/UpdateProductCommandHandler.cs index 8bd3a2a..6af07ea 100644 --- a/Hestia.Application/Handlers/Product/Commands/UpdateProduct/UpdateProductCommandHandler.cs +++ b/Hestia.Application/Handlers/Product/Commands/UpdateProduct/UpdateProductCommandHandler.cs @@ -1,6 +1,6 @@ using Hestia.Application.Interfaces.Product; using Hestia.Application.Models.Shared; -using MediatR; +using Hestia.Mediator.Infrastructure.Messaging; using System.Net; namespace Hestia.Application.Handlers.Product.Commands.UpdateProduct; diff --git a/Hestia.Application/Handlers/Product/Queries/GetProduct/GetProductQuery.cs b/Hestia.Application/Handlers/Product/Queries/GetProduct/GetProductQuery.cs index e9c1613..ed3fd34 100644 --- a/Hestia.Application/Handlers/Product/Queries/GetProduct/GetProductQuery.cs +++ b/Hestia.Application/Handlers/Product/Queries/GetProduct/GetProductQuery.cs @@ -1,6 +1,6 @@ using Hestia.Application.Models.Shared; using Hestia.Domain.Models.Product.Inbound.GetProduct; -using MediatR; +using Hestia.Mediator.Infrastructure.Messaging; namespace Hestia.Application.Handlers.Product.Queries.GetProduct; diff --git a/Hestia.Application/Handlers/Product/Queries/GetProduct/GetProductQueryHandler.cs b/Hestia.Application/Handlers/Product/Queries/GetProduct/GetProductQueryHandler.cs index 0bd56d7..11a813b 100644 --- a/Hestia.Application/Handlers/Product/Queries/GetProduct/GetProductQueryHandler.cs +++ b/Hestia.Application/Handlers/Product/Queries/GetProduct/GetProductQueryHandler.cs @@ -1,7 +1,7 @@ using Hestia.Application.Interfaces.Product; using Hestia.Application.Models.Shared; using Hestia.Domain.Models.Product.Inbound.GetProduct; -using MediatR; +using Hestia.Mediator.Infrastructure.Messaging; using System.Net; namespace Hestia.Application.Handlers.Product.Queries.GetProduct; diff --git a/Hestia.Application/Handlers/Validation/ValidationBehavior.cs b/Hestia.Application/Handlers/Validation/ValidationBehavior.cs index b26fc83..2165b1c 100644 --- a/Hestia.Application/Handlers/Validation/ValidationBehavior.cs +++ b/Hestia.Application/Handlers/Validation/ValidationBehavior.cs @@ -1,13 +1,17 @@ using FluentValidation; -using MediatR; +using Hestia.Mediator.Infrastructure.Messaging; +using Hestia.Mediator.Infrastructure.Pipeline; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using System.Reflection; namespace Hestia.Application.Handlers.Validation; -public class ValidationBehavior(IEnumerable> validators, IServiceProvider serviceProvider, ILogger> logger) - : IPipelineBehavior where TRequest : IRequest +public class ValidationBehavior( + IEnumerable> validators, + IServiceProvider serviceProvider, + ILogger> logger) : + IPipelineBehavior where TRequest : IRequest { public async Task Handle(TRequest request, RequestHandlerDelegate next, CancellationToken cancellationToken) { diff --git a/Hestia.Application/Hestia.Application.csproj b/Hestia.Application/Hestia.Application.csproj index 5e73513..aa30e87 100644 --- a/Hestia.Application/Hestia.Application.csproj +++ b/Hestia.Application/Hestia.Application.csproj @@ -7,18 +7,18 @@ - - - - - - - + + + + + + + diff --git a/Hestia.Application/Interfaces/Infrastructure/IAccessLayer.cs b/Hestia.Application/Interfaces/Infrastructure/IAccessLayer.cs deleted file mode 100644 index 47b1a68..0000000 --- a/Hestia.Application/Interfaces/Infrastructure/IAccessLayer.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace Hestia.Application.Interfaces.Infrastructure; - -public interface IAccessLayer : IRequestExecutor; \ No newline at end of file diff --git a/Hestia.Application/Interfaces/Infrastructure/ICoreLayer.cs b/Hestia.Application/Interfaces/Infrastructure/ICoreLayer.cs deleted file mode 100644 index 1e99476..0000000 --- a/Hestia.Application/Interfaces/Infrastructure/ICoreLayer.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace Hestia.Application.Interfaces.Infrastructure; - -public interface ICoreLayer : IRequestExecutor; \ No newline at end of file diff --git a/Hestia.Application/Profiles/Authentication/ApplicationUserProfile.cs b/Hestia.Application/Profiles/Authentication/ApplicationUserProfile.cs deleted file mode 100644 index 26795e7..0000000 --- a/Hestia.Application/Profiles/Authentication/ApplicationUserProfile.cs +++ /dev/null @@ -1,15 +0,0 @@ -using AutoMapper; -using Hestia.Application.Models.Authentication.Outbound; -using Hestia.Domain.Models.Authentication; - -namespace Hestia.Application.Profiles.Authentication; - -public class ApplicationUserProfile : Profile -{ - public ApplicationUserProfile() - { - CreateMap(); - CreateMap() - .ForMember(dest => dest.Token, opt => opt.Ignore()); - } -} \ No newline at end of file diff --git a/Hestia.Application/Profiles/Product/ProductProfile.cs b/Hestia.Application/Profiles/Product/ProductProfile.cs deleted file mode 100644 index 65c9904..0000000 --- a/Hestia.Application/Profiles/Product/ProductProfile.cs +++ /dev/null @@ -1,21 +0,0 @@ -using AutoMapper; -using Hestia.Domain.Models.Product.Inbound.GetProduct; -using Hestia.Domain.Models.Product.Inbound.UpdateProduct; - -namespace Hestia.Application.Profiles.Product; - -public class ProductProfile : Profile -{ - public ProductProfile() - { - CreateMap() - .ForMember(dest => dest.Id, opt => opt.Ignore()) - .ForMember(dest => dest.ExternalId, opt => opt.Ignore()) - .ForMember(dest => dest.DateCreated, opt => opt.Ignore()) - .ForMember(dest => dest.UserId, opt => opt.Ignore()) - .ForMember(dest => dest.DateEdited, opt => opt.MapFrom(src => DateTime.UtcNow)) - .ForAllMembers(opts => opts.Condition((src, dest, srcMember) => srcMember != null)); - - CreateMap(); - } -} \ No newline at end of file diff --git a/Hestia.Application/Services/Authentication/AuthService.cs b/Hestia.Application/Services/Authentication/AuthService.cs index 1175367..64a2cfe 100644 --- a/Hestia.Application/Services/Authentication/AuthService.cs +++ b/Hestia.Application/Services/Authentication/AuthService.cs @@ -1,13 +1,13 @@ -using AutoMapper; -using Hestia.Access.Requests.Shared; +using Hestia.Access.Requests.Shared; using Hestia.Access.Requests.User.Queries.UserExists; using Hestia.Access.Requests.User.Queries.ValidateUserLogin; using Hestia.Application.Interfaces.Authentication; -using Hestia.Application.Interfaces.Infrastructure; +using Hestia.Application.Mappers; using Hestia.Application.Models.Authentication.Inbound; using Hestia.Application.Models.Authentication.Outbound; using Hestia.Domain.Enumerations; using Hestia.Domain.Models.Authentication; +using Hestia.Mediator.Infrastructure.Layers; using Microsoft.Extensions.Logging; using System.Net; @@ -16,19 +16,17 @@ namespace Hestia.Application.Services.Authentication; public class AuthService( ITokenService tokenService, IUserService userService, - IMapper mapper, IAccessLayer accessLayer, ILogger logger) : IAuthService { public async Task<(ApplicationUserLoginResponseDto, HttpStatusCode)> LoginAsync(ApplicationUserLoginDto model, CancellationToken cancellationToken = default) { string? token = null!; - ApplicationUser? existingUser = null; - var statusCode = HttpStatusCode.NoContent; + HttpStatusCode statusCode; try { - existingUser = await GetExistingUserAsync(model.Username, model.Email, cancellationToken); + var existingUser = await GetExistingUserAsync(model.Username, model.Email, cancellationToken); if (existingUser is null) { @@ -55,7 +53,7 @@ public class AuthService( return (response, statusCode); } - public async Task<(ApplicationUserRegisterResponseDto, HttpStatusCode)> RegisterAsync(ApplicationUserRegisterDto model, Role? role = null, CancellationToken cancellationToken = default) + public async Task<(ApplicationUserRegisterResponseDto?, HttpStatusCode)> RegisterAsync(ApplicationUserRegisterDto model, Role? role = null, CancellationToken cancellationToken = default) { ApplicationUser? existingUser = null; var statusCode = HttpStatusCode.NoContent; @@ -83,7 +81,7 @@ public class AuthService( statusCode = HttpStatusCode.InternalServerError; } - var response = mapper.Map(existingUser); + var response = existingUser?.ToRegisterResponseDto(); return (response, statusCode); } diff --git a/Hestia.Application/Services/Authentication/TokenService.cs b/Hestia.Application/Services/Authentication/TokenService.cs index 2979217..b4effce 100644 --- a/Hestia.Application/Services/Authentication/TokenService.cs +++ b/Hestia.Application/Services/Authentication/TokenService.cs @@ -1,8 +1,8 @@ using Hestia.Access.Requests.Authentication.Commands.CreateTokenLog; using Hestia.Application.Interfaces.Authentication; -using Hestia.Application.Interfaces.Infrastructure; using Hestia.Application.Models.Shared; using Hestia.Domain.Models.Authentication; +using Hestia.Mediator.Infrastructure.Layers; using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Options; diff --git a/Hestia.Application/Services/Authentication/UserService.cs b/Hestia.Application/Services/Authentication/UserService.cs index 6cdf693..e78e434 100644 --- a/Hestia.Application/Services/Authentication/UserService.cs +++ b/Hestia.Application/Services/Authentication/UserService.cs @@ -1,9 +1,9 @@ using Hestia.Access.Entities.User; using Hestia.Access.Requests.User.Commands.CreateUser; using Hestia.Application.Interfaces.Authentication; -using Hestia.Application.Interfaces.Infrastructure; using Hestia.Application.Models.Authentication.Inbound; using Hestia.Domain.Models.Authentication; +using Hestia.Mediator.Infrastructure.Layers; using Microsoft.AspNetCore.Identity; namespace Hestia.Application.Services.Authentication; diff --git a/Hestia.Application/Services/Product/ProductService.cs b/Hestia.Application/Services/Product/ProductService.cs index 7f7a1b4..6df1b6f 100644 --- a/Hestia.Application/Services/Product/ProductService.cs +++ b/Hestia.Application/Services/Product/ProductService.cs @@ -1,19 +1,19 @@ -using AutoMapper; -using Hestia.Access.Requests.Product.Commands.CreateProduct; +using Hestia.Access.Requests.Product.Commands.CreateProduct; using Hestia.Access.Requests.Product.Commands.DeleteProduct; using Hestia.Access.Requests.Product.Commands.UpdateProduct; using Hestia.Access.Requests.Product.Queries.GetExisting; -using Hestia.Application.Interfaces.Infrastructure; using Hestia.Application.Interfaces.Product; +using Hestia.Application.Mappers; using Hestia.Domain.Models.Product.Inbound.CreateProduct; using Hestia.Domain.Models.Product.Inbound.GetProduct; using Hestia.Domain.Models.Product.Inbound.UpdateProduct; +using Hestia.Mediator.Infrastructure.Layers; using Microsoft.Extensions.Logging; using System.Net; namespace Hestia.Application.Services.Product; -public class ProductService(IAccessLayer accessLayer, IMapper mapper, ILogger logger) : IProductService +public class ProductService(IAccessLayer accessLayer, ILogger logger) : IProductService { public async Task<(Guid?, HttpStatusCode)> CreateProductAsync(CreateProductDto product, CancellationToken cancellationToken = default) { @@ -45,7 +45,7 @@ public class ProductService(IAccessLayer accessLayer, IMapper mapper, ILogger(existingProduct), HttpStatusCode.OK); + (existingProduct.ToResponseDto(), HttpStatusCode.OK); } catch (Exception ex) { @@ -114,7 +114,7 @@ public class ProductService(IAccessLayer accessLayer, IMapper mapper, ILogger GetExistingProductAsync(GetProductDto product, CancellationToken cancellationToken = default) => product.Id.HasValue && product.Id != Guid.Empty && !string.IsNullOrEmpty(product.ExternalId) ? await GetExistingProductAsync((Guid)product.Id, product.ExternalId, cancellationToken) : - !product.Id.HasValue || product.Id == Guid.Empty ? + (!product.Id.HasValue || product.Id == Guid.Empty) && !string.IsNullOrEmpty(product.ExternalId) ? await GetExistingProductAsync(product.ExternalId, cancellationToken) : string.IsNullOrEmpty(product.ExternalId) ? await GetExistingProductAsync(product.Id, cancellationToken) : null; diff --git a/Hestia.Background/Hestia.Background.csproj b/Hestia.Background/Hestia.Background.csproj index 636040b..d6837c6 100644 --- a/Hestia.Background/Hestia.Background.csproj +++ b/Hestia.Background/Hestia.Background.csproj @@ -7,7 +7,7 @@ - + diff --git a/Hestia.Domain/Hestia.Domain.csproj b/Hestia.Domain/Hestia.Domain.csproj index 01539f1..3c6b686 100644 --- a/Hestia.Domain/Hestia.Domain.csproj +++ b/Hestia.Domain/Hestia.Domain.csproj @@ -7,9 +7,8 @@ - - - + + diff --git a/Hestia.Mapper/Hestia.Mapper.csproj b/Hestia.Mapper/Hestia.Mapper.csproj new file mode 100644 index 0000000..bc7da11 --- /dev/null +++ b/Hestia.Mapper/Hestia.Mapper.csproj @@ -0,0 +1,15 @@ + + + + net9.0 + enable + enable + + + + + + + + + diff --git a/Hestia.Mapper/Mapper.cs b/Hestia.Mapper/Mapper.cs new file mode 100644 index 0000000..a03d33e --- /dev/null +++ b/Hestia.Mapper/Mapper.cs @@ -0,0 +1,5 @@ +namespace Hestia.Application.Mappers; + +public static partial class Mapper +{ +} \ No newline at end of file diff --git a/Hestia.Mapper/Mappers/ApplicationUser.cs b/Hestia.Mapper/Mappers/ApplicationUser.cs new file mode 100644 index 0000000..f62bf7b --- /dev/null +++ b/Hestia.Mapper/Mappers/ApplicationUser.cs @@ -0,0 +1,17 @@ +using Hestia.Application.Models.Authentication.Outbound; +using Hestia.Domain.Models.Authentication; + +namespace Hestia.Application.Mappers; + +public static partial class Mapper +{ + public static ApplicationUserRegisterResponseDto ToRegisterResponseDto(this ApplicationUser user) + { + return new ApplicationUserRegisterResponseDto + { + Username = user.UserName, + Email = user.Email, + Role = user.Role + }; + } +} \ No newline at end of file diff --git a/Hestia.Mapper/Mappers/Product.cs b/Hestia.Mapper/Mappers/Product.cs new file mode 100644 index 0000000..2ac3438 --- /dev/null +++ b/Hestia.Mapper/Mappers/Product.cs @@ -0,0 +1,30 @@ +using Hestia.Access.Entities.Product; +using Hestia.Domain.Models.Product.Inbound.GetProduct; +using Hestia.Domain.Models.Product.Inbound.UpdateProduct; + +namespace Hestia.Application.Mappers; + +public static partial class Mapper +{ + public static void ApplyUpdate(this Product product, UpdateProductDto dto) + { + product.Name = dto.Name; + product.Description = dto.Description; + product.Price = dto.Price; + product.DateEdited = DateTime.UtcNow; + } + + public static GetProductResponseDto ToResponseDto(this Product product) + { + return new GetProductResponseDto + { + Id = product.Id, + ExternalId = product.ExternalId, + DateCreated = product.DateCreated, + Name = product.Name, + Description = product.Description, + Price = product.Price, + DateEdited = product.DateEdited + }; + } +} \ No newline at end of file diff --git a/Hestia.Mediator/Hestia.Mediator.csproj b/Hestia.Mediator/Hestia.Mediator.csproj new file mode 100644 index 0000000..bf177e5 --- /dev/null +++ b/Hestia.Mediator/Hestia.Mediator.csproj @@ -0,0 +1,13 @@ + + + + net9.0 + enable + enable + + + + + + + diff --git a/Hestia.Mediator/Infrastructure/IMediator.cs b/Hestia.Mediator/Infrastructure/IMediator.cs new file mode 100644 index 0000000..3f37bc4 --- /dev/null +++ b/Hestia.Mediator/Infrastructure/IMediator.cs @@ -0,0 +1,9 @@ +using Hestia.Mediator.Infrastructure.Messaging; + +namespace Hestia.Mediator.Infrastructure; + +public interface IMediator +{ + Task Send(IRequest request, CancellationToken cancellationToken = default); + Task Send(IRequest request, CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/Hestia.Application/Interfaces/Infrastructure/IRequestExecutor.cs b/Hestia.Mediator/Infrastructure/IRequestExecutor.cs similarity index 75% rename from Hestia.Application/Interfaces/Infrastructure/IRequestExecutor.cs rename to Hestia.Mediator/Infrastructure/IRequestExecutor.cs index aadc1fb..9b2d119 100644 --- a/Hestia.Application/Interfaces/Infrastructure/IRequestExecutor.cs +++ b/Hestia.Mediator/Infrastructure/IRequestExecutor.cs @@ -1,6 +1,6 @@ -using MediatR; +using Hestia.Mediator.Infrastructure.Messaging; -namespace Hestia.Application.Interfaces.Infrastructure; +namespace Hestia.Mediator.Infrastructure; public interface IRequestExecutor { diff --git a/Hestia.Mediator/Infrastructure/Layers/IAccessLayer.cs b/Hestia.Mediator/Infrastructure/Layers/IAccessLayer.cs new file mode 100644 index 0000000..aa5da43 --- /dev/null +++ b/Hestia.Mediator/Infrastructure/Layers/IAccessLayer.cs @@ -0,0 +1,3 @@ +namespace Hestia.Mediator.Infrastructure.Layers; + +public interface IAccessLayer : IRequestExecutor; \ No newline at end of file diff --git a/Hestia.Mediator/Infrastructure/Layers/ICoreLayer.cs b/Hestia.Mediator/Infrastructure/Layers/ICoreLayer.cs new file mode 100644 index 0000000..19f6eda --- /dev/null +++ b/Hestia.Mediator/Infrastructure/Layers/ICoreLayer.cs @@ -0,0 +1,3 @@ +namespace Hestia.Mediator.Infrastructure.Layers; + +public interface ICoreLayer : IRequestExecutor; \ No newline at end of file diff --git a/Hestia.Application/Interfaces/Infrastructure/MediatrAdapter.cs b/Hestia.Mediator/Infrastructure/MediatorAdapter.cs similarity index 55% rename from Hestia.Application/Interfaces/Infrastructure/MediatrAdapter.cs rename to Hestia.Mediator/Infrastructure/MediatorAdapter.cs index 3911504..cd74ff3 100644 --- a/Hestia.Application/Interfaces/Infrastructure/MediatrAdapter.cs +++ b/Hestia.Mediator/Infrastructure/MediatorAdapter.cs @@ -1,15 +1,13 @@ -using MediatR; +using Hestia.Mediator.Infrastructure.Layers; +using Hestia.Mediator.Infrastructure.Messaging; -namespace Hestia.Application.Interfaces.Infrastructure; +namespace Hestia.Mediator.Infrastructure; -public class MediatrAdapter(IMediator mediator) : IAccessLayer, ICoreLayer +public class MediatorAdapter(IMediator mediator) : IAccessLayer, ICoreLayer { public Task ExecuteAsync(IRequest request, CancellationToken cancellationToken = default) => mediator.Send(request, cancellationToken); public Task ExecuteAsync(TRequest request, CancellationToken cancellationToken = default) where TRequest : IRequest => mediator.Send(request, cancellationToken); - - public Task PublishAsync(TNotification notification) where TNotification : INotification => - mediator.Publish(notification); } \ No newline at end of file diff --git a/Hestia.Mediator/Infrastructure/Messaging/IRequest.cs b/Hestia.Mediator/Infrastructure/Messaging/IRequest.cs new file mode 100644 index 0000000..b247e18 --- /dev/null +++ b/Hestia.Mediator/Infrastructure/Messaging/IRequest.cs @@ -0,0 +1,11 @@ +namespace Hestia.Mediator.Infrastructure.Messaging; + +public interface IRequest { } + +public interface IRequest { } + +public interface IRequestHandler + where TRequest : IRequest +{ + Task Handle(TRequest request, CancellationToken cancellationToken); +} diff --git a/Hestia.Mediator/Infrastructure/Pipeline/IPipelineBehavior.cs b/Hestia.Mediator/Infrastructure/Pipeline/IPipelineBehavior.cs new file mode 100644 index 0000000..56f29f6 --- /dev/null +++ b/Hestia.Mediator/Infrastructure/Pipeline/IPipelineBehavior.cs @@ -0,0 +1,12 @@ +namespace Hestia.Mediator.Infrastructure.Pipeline; + +public delegate Task RequestHandlerDelegate(); + +public interface IPipelineBehavior + where TRequest : Messaging.IRequest +{ + Task Handle( + TRequest request, + RequestHandlerDelegate next, + CancellationToken cancellationToken); +} \ No newline at end of file diff --git a/Hestia.Mediator/Infrastructure/Types/Unit.cs b/Hestia.Mediator/Infrastructure/Types/Unit.cs new file mode 100644 index 0000000..945cb68 --- /dev/null +++ b/Hestia.Mediator/Infrastructure/Types/Unit.cs @@ -0,0 +1,6 @@ +namespace Hestia.Mediator.Infrastructure.Types; + +public readonly struct Unit +{ + public static readonly Unit Value = new(); +} \ No newline at end of file diff --git a/Hestia.Mediator/Services/MediatorService.cs b/Hestia.Mediator/Services/MediatorService.cs new file mode 100644 index 0000000..69fdb1e --- /dev/null +++ b/Hestia.Mediator/Services/MediatorService.cs @@ -0,0 +1,55 @@ +using Hestia.Mediator.Infrastructure; +using Hestia.Mediator.Infrastructure.Messaging; +using Hestia.Mediator.Infrastructure.Types; +using System.Reflection; + +namespace Hestia.Mediator.Services; + +public class MediatorService(IServiceProvider serviceProvider) : IMediator +{ + public async Task Send(IRequest request, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(request); + + var handlerType = typeof(IRequestHandler<,>).MakeGenericType(request.GetType(), typeof(TResponse)); + + object? handler = serviceProvider.GetService(handlerType) ?? + throw new InvalidOperationException($"No handler registered for {request.GetType().Name} -> {typeof(TResponse).Name}"); + + var method = handlerType.GetMethod("Handle", BindingFlags.Instance | BindingFlags.Public) ?? + throw new InvalidOperationException($"Handler {handlerType.Name} does not have a public Handle method"); + + try + { + object? result = method.Invoke(handler, [request, cancellationToken]); + return await (Task)result!; + } + catch (TargetInvocationException ex) + { + throw ex.InnerException ?? ex; + } + } + + public async Task Send(IRequest request, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(request); + + var handlerType = typeof(IRequestHandler<,>).MakeGenericType(request.GetType(), typeof(Unit)); + + object? handler = serviceProvider.GetService(handlerType) ?? + throw new InvalidOperationException($"No handler registered for {request.GetType().Name} -> {nameof(Unit)}"); + + var method = handlerType.GetMethod("Handle", BindingFlags.Instance | BindingFlags.Public) ?? + throw new InvalidOperationException($"Handler {handlerType.Name} does not have a public Handle method"); + + try + { + object? result = method.Invoke(handler, [request, cancellationToken]); + await (Task)result!; + } + catch (TargetInvocationException ex) + { + throw ex.InnerException ?? ex; + } + } +} diff --git a/Hestia.Persistence.Tests/Handlers/Product/Commands/CreateProduct/CreateProductCommandHandlerTests.cs b/Hestia.Persistence.Tests/Handlers/Product/Commands/CreateProduct/CreateProductCommandHandlerTests.cs index e9954c3..5ae7a3d 100644 --- a/Hestia.Persistence.Tests/Handlers/Product/Commands/CreateProduct/CreateProductCommandHandlerTests.cs +++ b/Hestia.Persistence.Tests/Handlers/Product/Commands/CreateProduct/CreateProductCommandHandlerTests.cs @@ -1,37 +1,26 @@ using Hestia.Access.Requests.Product.Commands.CreateProduct; -using Hestia.Persistence.Contexts; using Hestia.Persistence.Handlers.Product.Commands.CreateProduct; using Hestia.Persistence.Handlers.Product.Queries.GetExisting; -using Microsoft.EntityFrameworkCore; +using Hestia.Persistence.Tests.Shared; using Microsoft.Extensions.Logging; using Moq; namespace Hestia.Persistence.Tests.Handlers.Product.Commands.CreateProduct; -public class CreateProductCommandHandlerTests : IDisposable +public class CreateProductCommandHandlerTests : RheaContextTestBase { - private readonly RheaContext _context; private readonly Mock> _mockLogger; private readonly CreateProductCommandHandler _handler; public CreateProductCommandHandlerTests() { - var dbContextOptions = new DbContextOptionsBuilder() - .UseSqlite("DataSource=:memory:") - .Options; - - _context = new RheaContext(dbContextOptions); - _context.Database.OpenConnection(); - _context.Database.EnsureCreated(); - _mockLogger = new Mock>(); - _handler = new CreateProductCommandHandler(_context, _mockLogger.Object); + _handler = new CreateProductCommandHandler(Context, _mockLogger.Object); } [Fact] public async Task Handle_ShouldAddProductToDatabase_WhenCalled() { - // Arrange var product = new Access.Entities.Product.Product(Guid.NewGuid()) { Name = "Test Product", @@ -44,23 +33,11 @@ public async Task Handle_ShouldAddProductToDatabase_WhenCalled() }; var command = new CreateProductCommand(product); - - // Act bool result = await _handler.Handle(command, CancellationToken.None); - // Assert Assert.True(result); - var savedProduct = await _context.Product.FindAsync(product.Id); + var savedProduct = await Context.Product.FindAsync(product.Id); Assert.NotNull(savedProduct); - Assert.Equal(product.Name, savedProduct.Name); - Assert.Equal(product.Description, savedProduct.Description); - Assert.Equal(product.Price, savedProduct.Price); - Assert.Equal(product.ExternalId, savedProduct.ExternalId); - } - - public void Dispose() - { - _context.Dispose(); } } \ No newline at end of file diff --git a/Hestia.Persistence.Tests/Handlers/Product/Commands/DeleteProduct/DeleteProductCommandHandlerTests.cs b/Hestia.Persistence.Tests/Handlers/Product/Commands/DeleteProduct/DeleteProductCommandHandlerTests.cs index 39b3eb4..cf29ae5 100644 --- a/Hestia.Persistence.Tests/Handlers/Product/Commands/DeleteProduct/DeleteProductCommandHandlerTests.cs +++ b/Hestia.Persistence.Tests/Handlers/Product/Commands/DeleteProduct/DeleteProductCommandHandlerTests.cs @@ -1,36 +1,25 @@ using Hestia.Access.Requests.Product.Commands.DeleteProduct; -using Hestia.Persistence.Contexts; using Hestia.Persistence.Handlers.Product.Commands.DeleteProduct; -using Microsoft.EntityFrameworkCore; +using Hestia.Persistence.Tests.Shared; using Microsoft.Extensions.Logging; using Moq; namespace Hestia.Persistence.Tests.Handlers.Product.Commands.DeleteProduct; -public class DeleteProductCommandHandlerTests : IDisposable +public class DeleteProductCommandHandlerTests : RheaContextTestBase { - private readonly RheaContext _context; private readonly Mock> _mockLogger; private readonly DeleteProductCommandHandler _handler; public DeleteProductCommandHandlerTests() { - var dbContextOptions = new DbContextOptionsBuilder() - .UseSqlite("DataSource=:memory:") - .Options; - - _context = new RheaContext(dbContextOptions); - _context.Database.OpenConnection(); - _context.Database.EnsureCreated(); - _mockLogger = new Mock>(); - _handler = new DeleteProductCommandHandler(_context, _mockLogger.Object); + _handler = new DeleteProductCommandHandler(Context, _mockLogger.Object); } [Fact] public async Task Handle_ShouldRemoveProductFromDatabase_WhenCalled() { - // Arrange var product = new Access.Entities.Product.Product(Guid.NewGuid()) { Name = "Test Product", @@ -42,23 +31,15 @@ public async Task Handle_ShouldRemoveProductFromDatabase_WhenCalled() DateEdited = DateTime.UtcNow }; - await _context.Product.AddAsync(product); - await _context.SaveChangesAsync(); + await Context.Product.AddAsync(product); + await Context.SaveChangesAsync(); var command = new DeleteProductCommand(product); - - // Act bool result = await _handler.Handle(command, CancellationToken.None); - // Assert Assert.True(result); - var deletedProduct = await _context.Product.FindAsync(product.Id); - Assert.Null(deletedProduct); // Ensure the product is deleted - } - - public void Dispose() - { - _context.Dispose(); + var deletedProduct = await Context.Product.FindAsync(product.Id); + Assert.Null(deletedProduct); } -} \ No newline at end of file +} diff --git a/Hestia.Persistence.Tests/Handlers/Product/Commands/UpdateProduct/UpdateProductCommandHandlerTests.cs b/Hestia.Persistence.Tests/Handlers/Product/Commands/UpdateProduct/UpdateProductCommandHandlerTests.cs index 98d94e3..f3470fd 100644 --- a/Hestia.Persistence.Tests/Handlers/Product/Commands/UpdateProduct/UpdateProductCommandHandlerTests.cs +++ b/Hestia.Persistence.Tests/Handlers/Product/Commands/UpdateProduct/UpdateProductCommandHandlerTests.cs @@ -1,36 +1,25 @@ using Hestia.Access.Requests.Product.Commands.UpdateProduct; -using Hestia.Persistence.Contexts; using Hestia.Persistence.Handlers.Product.Commands.UpdateProduct; -using Microsoft.EntityFrameworkCore; +using Hestia.Persistence.Tests.Shared; using Microsoft.Extensions.Logging; using Moq; namespace Hestia.Persistence.Tests.Handlers.Product.Commands.UpdateProduct; -public class UpdateProductCommandHandlerTests : IDisposable +public class UpdateProductCommandHandlerTests : RheaContextTestBase { - private readonly RheaContext _context; private readonly Mock> _mockLogger; private readonly UpdateProductCommandHandler _handler; public UpdateProductCommandHandlerTests() { - var dbContextOptions = new DbContextOptionsBuilder() - .UseSqlite("DataSource=:memory:") - .Options; - - _context = new RheaContext(dbContextOptions); - _context.Database.OpenConnection(); - _context.Database.EnsureCreated(); - _mockLogger = new Mock>(); - _handler = new UpdateProductCommandHandler(_context, _mockLogger.Object); + _handler = new UpdateProductCommandHandler(Context, _mockLogger.Object); } [Fact] public async Task Handle_ShouldUpdateProductInDatabase_WhenCalled() { - // Arrange var product = new Access.Entities.Product.Product(Guid.NewGuid()) { Name = "Test Product", @@ -42,32 +31,23 @@ public async Task Handle_ShouldUpdateProductInDatabase_WhenCalled() DateEdited = DateTime.UtcNow }; - await _context.Product.AddAsync(product); - await _context.SaveChangesAsync(); + await Context.Product.AddAsync(product); + await Context.SaveChangesAsync(); - // Update product details product.Name = "Updated Product"; product.Description = "Updated Description"; product.Price = 79.99m; var command = new UpdateProductCommand(product); - - // Act bool result = await _handler.Handle(command, CancellationToken.None); - // Assert Assert.True(result); - var updatedProduct = await _context.Product.FindAsync(product.Id); + var updatedProduct = await Context.Product.FindAsync(product.Id); Assert.NotNull(updatedProduct); Assert.Equal(product.Name, updatedProduct.Name); Assert.Equal(product.Description, updatedProduct.Description); Assert.Equal(product.Price, updatedProduct.Price); Assert.Equal(product.ExternalId, updatedProduct.ExternalId); } - - public void Dispose() - { - _context.Dispose(); - } } \ No newline at end of file diff --git a/Hestia.Persistence.Tests/Handlers/Product/Queries/GetExisting/GetExistingProductByCompositeIdQueryHandlerTests.cs b/Hestia.Persistence.Tests/Handlers/Product/Queries/GetExisting/GetExistingProductByCompositeIdQueryHandlerTests.cs index 28ce037..c5da369 100644 --- a/Hestia.Persistence.Tests/Handlers/Product/Queries/GetExisting/GetExistingProductByCompositeIdQueryHandlerTests.cs +++ b/Hestia.Persistence.Tests/Handlers/Product/Queries/GetExisting/GetExistingProductByCompositeIdQueryHandlerTests.cs @@ -1,36 +1,25 @@ using Hestia.Access.Requests.Product.Queries.GetExisting; -using Hestia.Persistence.Contexts; using Hestia.Persistence.Handlers.Product.Queries.GetExisting; -using Microsoft.EntityFrameworkCore; +using Hestia.Persistence.Tests.Shared; using Microsoft.Extensions.Logging; using Moq; namespace Hestia.Persistence.Tests.Handlers.Product.Queries.GetExisting; -public class GetExistingProductByCompositeIdQueryHandlerTests : IDisposable +public class GetExistingProductByCompositeIdQueryHandlerTests : RheaContextTestBase { - private readonly RheaContext _context; private readonly Mock> _mockLogger; private readonly GetExistingProductByCompositeIdQueryHandler _handler; public GetExistingProductByCompositeIdQueryHandlerTests() { - var dbContextOptions = new DbContextOptionsBuilder() - .UseSqlite("DataSource=:memory:") - .Options; - - _context = new RheaContext(dbContextOptions); - _context.Database.OpenConnection(); - _context.Database.EnsureCreated(); - _mockLogger = new Mock>(); - _handler = new GetExistingProductByCompositeIdQueryHandler(_context, _mockLogger.Object); + _handler = new GetExistingProductByCompositeIdQueryHandler(Context, _mockLogger.Object); } [Fact] public async Task Handle_ShouldReturnProduct_WhenProductExists() { - // Arrange var product = new Access.Entities.Product.Product(Guid.NewGuid()) { Name = "Test Product", @@ -42,15 +31,12 @@ public async Task Handle_ShouldReturnProduct_WhenProductExists() DateEdited = DateTime.UtcNow }; - await _context.Product.AddAsync(product); - await _context.SaveChangesAsync(); + await Context.Product.AddAsync(product); + await Context.SaveChangesAsync(); var query = new GetExistingProductByCompositeIdQuery(product.Id, product.ExternalId); - - // Act var result = await _handler.Handle(query, CancellationToken.None); - // Assert Assert.NotNull(result); Assert.Equal(product.Id, result?.Id); Assert.Equal(product.ExternalId, result?.ExternalId); @@ -59,18 +45,9 @@ public async Task Handle_ShouldReturnProduct_WhenProductExists() [Fact] public async Task Handle_ShouldReturnNull_WhenProductDoesNotExist() { - // Arrange var query = new GetExistingProductByCompositeIdQuery(Guid.NewGuid(), "nonexistent-external-id"); - - // Act var result = await _handler.Handle(query, CancellationToken.None); - // Assert Assert.Null(result); } - - public void Dispose() - { - _context.Dispose(); - } -} +} \ No newline at end of file diff --git a/Hestia.Persistence.Tests/Handlers/Product/Queries/GetExisting/GetExistingProductByExternalIdQueryHandlerTests.cs b/Hestia.Persistence.Tests/Handlers/Product/Queries/GetExisting/GetExistingProductByExternalIdQueryHandlerTests.cs index 0371f3a..06157d8 100644 --- a/Hestia.Persistence.Tests/Handlers/Product/Queries/GetExisting/GetExistingProductByExternalIdQueryHandlerTests.cs +++ b/Hestia.Persistence.Tests/Handlers/Product/Queries/GetExisting/GetExistingProductByExternalIdQueryHandlerTests.cs @@ -1,36 +1,25 @@ using Hestia.Access.Requests.Product.Queries.GetExisting; -using Hestia.Persistence.Contexts; using Hestia.Persistence.Handlers.Product.Queries.GetExisting; -using Microsoft.EntityFrameworkCore; +using Hestia.Persistence.Tests.Shared; using Microsoft.Extensions.Logging; using Moq; namespace Hestia.Persistence.Tests.Handlers.Product.Queries.GetExisting; -public class GetExistingProductByExternalIdQueryHandlerTests : IDisposable +public class GetExistingProductByExternalIdQueryHandlerTests : RheaContextTestBase { - private readonly RheaContext _context; private readonly Mock> _mockLogger; private readonly GetExistingProductByExternalIdQueryHandler _handler; public GetExistingProductByExternalIdQueryHandlerTests() { - var dbContextOptions = new DbContextOptionsBuilder() - .UseSqlite("DataSource=:memory:") - .Options; - - _context = new RheaContext(dbContextOptions); - _context.Database.OpenConnection(); - _context.Database.EnsureCreated(); - _mockLogger = new Mock>(); - _handler = new GetExistingProductByExternalIdQueryHandler(_context, _mockLogger.Object); + _handler = new GetExistingProductByExternalIdQueryHandler(Context, _mockLogger.Object); } [Fact] public async Task Handle_ShouldReturnProduct_WhenProductExists() { - // Arrange var product = new Access.Entities.Product.Product(Guid.NewGuid()) { Name = "Test Product", @@ -42,15 +31,12 @@ public async Task Handle_ShouldReturnProduct_WhenProductExists() DateEdited = DateTime.UtcNow }; - await _context.Product.AddAsync(product); - await _context.SaveChangesAsync(); + await Context.Product.AddAsync(product); + await Context.SaveChangesAsync(); var query = new GetExistingProductByExternalIdQuery(product.ExternalId); - - // Act var result = await _handler.Handle(query, CancellationToken.None); - // Assert Assert.NotNull(result); Assert.Equal(product.ExternalId, result?.ExternalId); } @@ -58,18 +44,9 @@ public async Task Handle_ShouldReturnProduct_WhenProductExists() [Fact] public async Task Handle_ShouldReturnNull_WhenProductDoesNotExist() { - // Arrange var query = new GetExistingProductByExternalIdQuery("nonexistent-external-id"); - - // Act var result = await _handler.Handle(query, CancellationToken.None); - // Assert Assert.Null(result); } - - public void Dispose() - { - _context.Dispose(); - } } \ No newline at end of file diff --git a/Hestia.Persistence.Tests/Handlers/Product/Queries/GetExisting/GetExistingProductByIdQueryHandlerTests.cs b/Hestia.Persistence.Tests/Handlers/Product/Queries/GetExisting/GetExistingProductByIdQueryHandlerTests.cs index cf83ebc..d1d862b 100644 --- a/Hestia.Persistence.Tests/Handlers/Product/Queries/GetExisting/GetExistingProductByIdQueryHandlerTests.cs +++ b/Hestia.Persistence.Tests/Handlers/Product/Queries/GetExisting/GetExistingProductByIdQueryHandlerTests.cs @@ -1,36 +1,25 @@ using Hestia.Access.Requests.Product.Queries.GetExisting; -using Hestia.Persistence.Contexts; using Hestia.Persistence.Handlers.Product.Queries.GetExisting; -using Microsoft.EntityFrameworkCore; +using Hestia.Persistence.Tests.Shared; using Microsoft.Extensions.Logging; using Moq; namespace Hestia.Persistence.Tests.Handlers.Product.Queries.GetExisting; -public class GetExistingProductByIdQueryHandlerTests : IDisposable +public class GetExistingProductByIdQueryHandlerTests : RheaContextTestBase { - private readonly RheaContext _context; private readonly Mock> _mockLogger; private readonly GetExistingProductByIdQueryHandler _handler; public GetExistingProductByIdQueryHandlerTests() { - var dbContextOptions = new DbContextOptionsBuilder() - .UseSqlite("DataSource=:memory:") - .Options; - - _context = new RheaContext(dbContextOptions); - _context.Database.OpenConnection(); - _context.Database.EnsureCreated(); - _mockLogger = new Mock>(); - _handler = new GetExistingProductByIdQueryHandler(_context, _mockLogger.Object); + _handler = new GetExistingProductByIdQueryHandler(Context, _mockLogger.Object); } [Fact] public async Task Handle_ShouldReturnProduct_WhenProductExists() { - // Arrange var product = new Access.Entities.Product.Product(Guid.NewGuid()) { Name = "Test Product", @@ -42,15 +31,12 @@ public async Task Handle_ShouldReturnProduct_WhenProductExists() DateEdited = DateTime.UtcNow }; - await _context.Product.AddAsync(product); - await _context.SaveChangesAsync(); + await Context.Product.AddAsync(product); + await Context.SaveChangesAsync(); var query = new GetExistingProductByIdQuery(product.Id); - - // Act var result = await _handler.Handle(query, CancellationToken.None); - // Assert Assert.NotNull(result); Assert.Equal(product.Id, result?.Id); } @@ -58,18 +44,9 @@ public async Task Handle_ShouldReturnProduct_WhenProductExists() [Fact] public async Task Handle_ShouldReturnNull_WhenProductDoesNotExist() { - // Arrange var query = new GetExistingProductByIdQuery(Guid.NewGuid()); - - // Act var result = await _handler.Handle(query, CancellationToken.None); - // Assert Assert.Null(result); } - - public void Dispose() - { - _context.Dispose(); - } -} +} \ No newline at end of file diff --git a/Hestia.Persistence.Tests/Handlers/Product/Queries/GetExisting/GetExistingProductByUserQueryHandlerTests.cs b/Hestia.Persistence.Tests/Handlers/Product/Queries/GetExisting/GetExistingProductByUserQueryHandlerTests.cs index 2ad74f1..33f5081 100644 --- a/Hestia.Persistence.Tests/Handlers/Product/Queries/GetExisting/GetExistingProductByUserQueryHandlerTests.cs +++ b/Hestia.Persistence.Tests/Handlers/Product/Queries/GetExisting/GetExistingProductByUserQueryHandlerTests.cs @@ -1,36 +1,25 @@ using Hestia.Access.Requests.Product.Queries.GetExisting; -using Hestia.Persistence.Contexts; using Hestia.Persistence.Handlers.Product.Queries.GetExisting; -using Microsoft.EntityFrameworkCore; +using Hestia.Persistence.Tests.Shared; using Microsoft.Extensions.Logging; using Moq; namespace Hestia.Persistence.Tests.Handlers.Product.Queries.GetExisting; -public class GetExistingProductByUserQueryHandlerTests : IDisposable +public class GetExistingProductByUserQueryHandlerTests : RheaContextTestBase { - private readonly RheaContext _context; private readonly Mock> _mockLogger; private readonly GetExistingProductByUserQueryHandler _handler; public GetExistingProductByUserQueryHandlerTests() { - var dbContextOptions = new DbContextOptionsBuilder() - .UseSqlite("DataSource=:memory:") - .Options; - - _context = new RheaContext(dbContextOptions); - _context.Database.OpenConnection(); - _context.Database.EnsureCreated(); - _mockLogger = new Mock>(); - _handler = new GetExistingProductByUserQueryHandler(_context, _mockLogger.Object); + _handler = new GetExistingProductByUserQueryHandler(Context, _mockLogger.Object); } [Fact] public async Task Handle_ShouldReturnProduct_WhenProductExists() { - // Arrange var product = new Access.Entities.Product.Product(Guid.NewGuid()) { Name = "Test Product", @@ -42,15 +31,12 @@ public async Task Handle_ShouldReturnProduct_WhenProductExists() DateEdited = DateTime.UtcNow }; - await _context.Product.AddAsync(product); - await _context.SaveChangesAsync(); + await Context.Product.AddAsync(product); + await Context.SaveChangesAsync(); var query = new GetExistingProductByUserQuery(product.UserId, product.ExternalId); - - // Act var result = await _handler.Handle(query, CancellationToken.None); - // Assert Assert.NotNull(result); Assert.Equal(product.UserId, result?.UserId); Assert.Equal(product.ExternalId, result?.ExternalId); @@ -59,18 +45,9 @@ public async Task Handle_ShouldReturnProduct_WhenProductExists() [Fact] public async Task Handle_ShouldReturnNull_WhenProductDoesNotExist() { - // Arrange var query = new GetExistingProductByUserQuery("nonexistent-user-id", "nonexistent-external-id"); - - // Act var result = await _handler.Handle(query, CancellationToken.None); - // Assert Assert.Null(result); } - - public void Dispose() - { - _context.Dispose(); - } } \ No newline at end of file diff --git a/Hestia.Persistence.Tests/Hestia.Persistence.Tests.csproj b/Hestia.Persistence.Tests/Hestia.Persistence.Tests.csproj index 53106a9..61aec92 100644 --- a/Hestia.Persistence.Tests/Hestia.Persistence.Tests.csproj +++ b/Hestia.Persistence.Tests/Hestia.Persistence.Tests.csproj @@ -8,12 +8,18 @@ - - - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + - - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/Hestia.Persistence.Tests/Shared/RheaContextTestBase.cs b/Hestia.Persistence.Tests/Shared/RheaContextTestBase.cs new file mode 100644 index 0000000..185da80 --- /dev/null +++ b/Hestia.Persistence.Tests/Shared/RheaContextTestBase.cs @@ -0,0 +1,22 @@ +using Hestia.Persistence.Contexts; +using Microsoft.EntityFrameworkCore; + +namespace Hestia.Persistence.Tests.Shared; + +public abstract class RheaContextTestBase : IDisposable +{ + protected readonly RheaContext Context; + + protected RheaContextTestBase() + { + var dbContextOptions = new DbContextOptionsBuilder() + .UseSqlite("DataSource=:memory:") + .Options; + + Context = new RheaContext(dbContextOptions); + Context.Database.OpenConnection(); + Context.Database.EnsureCreated(); + } + + public void Dispose() => Context.Dispose(); +} \ No newline at end of file diff --git a/Hestia.Persistence/Contexts/HestiaContext.cs b/Hestia.Persistence/Contexts/HestiaContext.cs index 884f9b0..98d489a 100644 --- a/Hestia.Persistence/Contexts/HestiaContext.cs +++ b/Hestia.Persistence/Contexts/HestiaContext.cs @@ -3,38 +3,16 @@ using Hestia.Domain.Models.Authentication; using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using System.Reflection; namespace Hestia.Persistence.Contexts; -public class HestiaContext : IdentityUserContext +public class HestiaContext(DbContextOptions options) : IdentityUserContext(options) { - private readonly IConfiguration configuration; - public DbSet TokenLog { get; set; } = null!; public DbSet User { get; set; } = null!; - public HestiaContext() - { - } - - public HestiaContext(IConfiguration configuration) => this.configuration = configuration; - - public HestiaContext(DbContextOptions options) - : base(options) - { - } - - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - { - if (optionsBuilder.IsConfigured) - return; - - optionsBuilder.UseNpgsql(configuration.GetConnectionString("AuthServer")); - } - protected override void OnModelCreating(ModelBuilder builder) { base.OnModelCreating(builder); @@ -43,20 +21,18 @@ protected override void OnModelCreating(ModelBuilder builder) public async Task ExecuteInTransactionAsync(Func> action, ILogger logger) { - using (var transaction = await Database.BeginTransactionAsync()) + using var transaction = await Database.BeginTransactionAsync(); + try + { + var result = await action(this); + await transaction.CommitAsync(); + return result; + } + catch (Exception ex) { - try - { - var result = await action(this); - await transaction.CommitAsync(); - return result; - } - catch (Exception ex) - { - await transaction.RollbackAsync(); - logger.LogCritical(ex, "An error occurred while executing the database action: {Message} | Context: {Context}", ex.Message, typeof(T).Name); - throw; - } + await transaction.RollbackAsync(); + logger.LogCritical(ex, "An error occurred while executing the database action: {Message} | Context: {Context}", ex.Message, typeof(T).Name); + throw; } } } \ No newline at end of file diff --git a/Hestia.Persistence/Contexts/RheaContext.cs b/Hestia.Persistence/Contexts/RheaContext.cs index ab18cfc..095d544 100644 --- a/Hestia.Persistence/Contexts/RheaContext.cs +++ b/Hestia.Persistence/Contexts/RheaContext.cs @@ -1,57 +1,36 @@ using Hestia.Access.Entities.Product; using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using System.Reflection; namespace Hestia.Persistence.Contexts; -public class RheaContext : DbContext +public class RheaContext(DbContextOptions options) : DbContext(options) { - private readonly IConfiguration configuration; public DbSet Product { get; set; } = null!; - public RheaContext() - { - } - - public RheaContext(IConfiguration configuration) => this.configuration = configuration; - - public RheaContext(DbContextOptions options) - : base(options) - { - } - - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - { - if (optionsBuilder.IsConfigured) - return; - - optionsBuilder.UseNpgsql(configuration.GetConnectionString("DataServer")); - } - protected override void OnModelCreating(ModelBuilder builder) { base.OnModelCreating(builder); builder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly()); } - public async Task ExecuteInTransactionAsync(Func> action, ILogger logger) + public async Task ExecuteInTransactionAsync( + Func> action, + ILogger logger) { - using (var transaction = await Database.BeginTransactionAsync()) + using var transaction = await Database.BeginTransactionAsync(); + try + { + var result = await action(this); + await transaction.CommitAsync(); + return result; + } + catch (Exception ex) { - try - { - var result = await action(this); - await transaction.CommitAsync(); - return result; - } - catch (Exception ex) - { - await transaction.RollbackAsync(); - logger.LogCritical(ex, "An error occurred while executing the database action: {Message} | Context: {Context}", ex.Message, typeof(T).Name); - throw; - } + await transaction.RollbackAsync(); + logger.LogCritical(ex, "An error occurred while executing the database action: {Message} | Context: {Context}", ex.Message, typeof(T).Name); + throw; } } } \ No newline at end of file diff --git a/Hestia.Persistence/Handlers/Authentication/Commands/CreateTokenLog/CreateOrUpdateTokenLogCommandHandler.cs b/Hestia.Persistence/Handlers/Authentication/Commands/CreateTokenLog/CreateOrUpdateTokenLogCommandHandler.cs index a0a6711..9a316e6 100644 --- a/Hestia.Persistence/Handlers/Authentication/Commands/CreateTokenLog/CreateOrUpdateTokenLogCommandHandler.cs +++ b/Hestia.Persistence/Handlers/Authentication/Commands/CreateTokenLog/CreateOrUpdateTokenLogCommandHandler.cs @@ -1,7 +1,8 @@ using Hestia.Access.Entities.Authentication; using Hestia.Access.Requests.Authentication.Commands.CreateTokenLog; +using Hestia.Mediator.Infrastructure.Messaging; +using Hestia.Mediator.Infrastructure.Types; using Hestia.Persistence.Contexts; -using MediatR; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; diff --git a/Hestia.Persistence/Handlers/Product/Commands/CreateProduct/CreateProductCommandHandler.cs b/Hestia.Persistence/Handlers/Product/Commands/CreateProduct/CreateProductCommandHandler.cs index 1469424..e1e68f4 100644 --- a/Hestia.Persistence/Handlers/Product/Commands/CreateProduct/CreateProductCommandHandler.cs +++ b/Hestia.Persistence/Handlers/Product/Commands/CreateProduct/CreateProductCommandHandler.cs @@ -1,7 +1,7 @@ using Hestia.Access.Requests.Product.Commands.CreateProduct; +using Hestia.Mediator.Infrastructure.Messaging; using Hestia.Persistence.Contexts; using Hestia.Persistence.Handlers.Product.Queries.GetExisting; -using MediatR; using Microsoft.Extensions.Logging; namespace Hestia.Persistence.Handlers.Product.Commands.CreateProduct; diff --git a/Hestia.Persistence/Handlers/Product/Commands/DeleteProduct/DeleteProductCommandHandler.cs b/Hestia.Persistence/Handlers/Product/Commands/DeleteProduct/DeleteProductCommandHandler.cs index 588335c..77846a5 100644 --- a/Hestia.Persistence/Handlers/Product/Commands/DeleteProduct/DeleteProductCommandHandler.cs +++ b/Hestia.Persistence/Handlers/Product/Commands/DeleteProduct/DeleteProductCommandHandler.cs @@ -1,6 +1,6 @@ using Hestia.Access.Requests.Product.Commands.DeleteProduct; +using Hestia.Mediator.Infrastructure.Messaging; using Hestia.Persistence.Contexts; -using MediatR; using Microsoft.Extensions.Logging; namespace Hestia.Persistence.Handlers.Product.Commands.DeleteProduct; diff --git a/Hestia.Persistence/Handlers/Product/Commands/UpdateProduct/UpdateProductCommandHandler.cs b/Hestia.Persistence/Handlers/Product/Commands/UpdateProduct/UpdateProductCommandHandler.cs index f6c07cd..3365d70 100644 --- a/Hestia.Persistence/Handlers/Product/Commands/UpdateProduct/UpdateProductCommandHandler.cs +++ b/Hestia.Persistence/Handlers/Product/Commands/UpdateProduct/UpdateProductCommandHandler.cs @@ -1,6 +1,6 @@ using Hestia.Access.Requests.Product.Commands.UpdateProduct; +using Hestia.Mediator.Infrastructure.Messaging; using Hestia.Persistence.Contexts; -using MediatR; using Microsoft.Extensions.Logging; namespace Hestia.Persistence.Handlers.Product.Commands.UpdateProduct; diff --git a/Hestia.Persistence/Handlers/Product/Queries/GetExisting/GetExistingProductByCompositeIdQueryHandler.cs b/Hestia.Persistence/Handlers/Product/Queries/GetExisting/GetExistingProductByCompositeIdQueryHandler.cs index b2448ae..3df330c 100644 --- a/Hestia.Persistence/Handlers/Product/Queries/GetExisting/GetExistingProductByCompositeIdQueryHandler.cs +++ b/Hestia.Persistence/Handlers/Product/Queries/GetExisting/GetExistingProductByCompositeIdQueryHandler.cs @@ -1,6 +1,6 @@ using Hestia.Access.Requests.Product.Queries.GetExisting; +using Hestia.Mediator.Infrastructure.Messaging; using Hestia.Persistence.Contexts; -using MediatR; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; diff --git a/Hestia.Persistence/Handlers/Product/Queries/GetExisting/GetExistingProductByExternalIdQueryHandler.cs b/Hestia.Persistence/Handlers/Product/Queries/GetExisting/GetExistingProductByExternalIdQueryHandler.cs index 11c1609..e325a79 100644 --- a/Hestia.Persistence/Handlers/Product/Queries/GetExisting/GetExistingProductByExternalIdQueryHandler.cs +++ b/Hestia.Persistence/Handlers/Product/Queries/GetExisting/GetExistingProductByExternalIdQueryHandler.cs @@ -1,6 +1,6 @@ using Hestia.Access.Requests.Product.Queries.GetExisting; +using Hestia.Mediator.Infrastructure.Messaging; using Hestia.Persistence.Contexts; -using MediatR; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; diff --git a/Hestia.Persistence/Handlers/Product/Queries/GetExisting/GetExistingProductByIdQueryHandler.cs b/Hestia.Persistence/Handlers/Product/Queries/GetExisting/GetExistingProductByIdQueryHandler.cs index 618533e..11170d1 100644 --- a/Hestia.Persistence/Handlers/Product/Queries/GetExisting/GetExistingProductByIdQueryHandler.cs +++ b/Hestia.Persistence/Handlers/Product/Queries/GetExisting/GetExistingProductByIdQueryHandler.cs @@ -1,6 +1,6 @@ using Hestia.Access.Requests.Product.Queries.GetExisting; +using Hestia.Mediator.Infrastructure.Messaging; using Hestia.Persistence.Contexts; -using MediatR; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; diff --git a/Hestia.Persistence/Handlers/Product/Queries/GetExisting/GetExistingProductByUserQueryHandler.cs b/Hestia.Persistence/Handlers/Product/Queries/GetExisting/GetExistingProductByUserQueryHandler.cs index fdf58a5..4434574 100644 --- a/Hestia.Persistence/Handlers/Product/Queries/GetExisting/GetExistingProductByUserQueryHandler.cs +++ b/Hestia.Persistence/Handlers/Product/Queries/GetExisting/GetExistingProductByUserQueryHandler.cs @@ -1,6 +1,6 @@ using Hestia.Access.Requests.Product.Queries.GetExisting; +using Hestia.Mediator.Infrastructure.Messaging; using Hestia.Persistence.Contexts; -using MediatR; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; diff --git a/Hestia.Persistence/Handlers/Shared/ExecuteSaveChangesAsyncHandler.cs b/Hestia.Persistence/Handlers/Shared/ExecuteSaveChangesAsyncHandler.cs index 2397a61..c6f9dae 100644 --- a/Hestia.Persistence/Handlers/Shared/ExecuteSaveChangesAsyncHandler.cs +++ b/Hestia.Persistence/Handlers/Shared/ExecuteSaveChangesAsyncHandler.cs @@ -1,6 +1,7 @@ using Hestia.Access.Requests.Shared; +using Hestia.Mediator.Infrastructure.Messaging; +using Hestia.Mediator.Infrastructure.Types; using Hestia.Persistence.Contexts; -using MediatR; using Microsoft.Extensions.Logging; namespace Hestia.Persistence.Handlers.Shared; diff --git a/Hestia.Persistence/Handlers/User/Commands/CreateUser/CreateApplicationUserCommandHandler.cs b/Hestia.Persistence/Handlers/User/Commands/CreateUser/CreateApplicationUserCommandHandler.cs index ff05e5b..ac955ef 100644 --- a/Hestia.Persistence/Handlers/User/Commands/CreateUser/CreateApplicationUserCommandHandler.cs +++ b/Hestia.Persistence/Handlers/User/Commands/CreateUser/CreateApplicationUserCommandHandler.cs @@ -1,6 +1,6 @@ using Hestia.Access.Requests.User.Commands.CreateUser; using Hestia.Domain.Models.Authentication; -using MediatR; +using Hestia.Mediator.Infrastructure.Messaging; using Microsoft.AspNetCore.Identity; namespace Hestia.Persistence.Handlers.User.Commands.CreateUser; diff --git a/Hestia.Persistence/Handlers/User/Commands/CreateUser/CreateInternalUserCommandHandler.cs b/Hestia.Persistence/Handlers/User/Commands/CreateUser/CreateInternalUserCommandHandler.cs index 9db92a1..6a94631 100644 --- a/Hestia.Persistence/Handlers/User/Commands/CreateUser/CreateInternalUserCommandHandler.cs +++ b/Hestia.Persistence/Handlers/User/Commands/CreateUser/CreateInternalUserCommandHandler.cs @@ -1,6 +1,6 @@ using Hestia.Access.Requests.User.Commands.CreateUser; +using Hestia.Mediator.Infrastructure.Messaging; using Hestia.Persistence.Contexts; -using MediatR; using Microsoft.Extensions.Logging; namespace Hestia.Persistence.Handlers.User.Commands.CreateUser; diff --git a/Hestia.Persistence/Handlers/User/Queries/UserExists/GetExistingUserQueryHandler.cs b/Hestia.Persistence/Handlers/User/Queries/UserExists/GetExistingUserQueryHandler.cs index 65da54a..7d7c669 100644 --- a/Hestia.Persistence/Handlers/User/Queries/UserExists/GetExistingUserQueryHandler.cs +++ b/Hestia.Persistence/Handlers/User/Queries/UserExists/GetExistingUserQueryHandler.cs @@ -1,7 +1,7 @@ using Hestia.Access.Requests.User.Queries.UserExists; using Hestia.Domain.Models.Authentication; +using Hestia.Mediator.Infrastructure.Messaging; using Hestia.Persistence.Contexts; -using MediatR; using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Logging; diff --git a/Hestia.Persistence/Handlers/User/Queries/ValidateUserLogin/ValidateUserLoginQueryHandler.cs b/Hestia.Persistence/Handlers/User/Queries/ValidateUserLogin/ValidateUserLoginQueryHandler.cs index 0ecc3a7..c94910f 100644 --- a/Hestia.Persistence/Handlers/User/Queries/ValidateUserLogin/ValidateUserLoginQueryHandler.cs +++ b/Hestia.Persistence/Handlers/User/Queries/ValidateUserLogin/ValidateUserLoginQueryHandler.cs @@ -1,13 +1,11 @@ using Hestia.Access.Requests.User.Queries.ValidateUserLogin; using Hestia.Domain.Models.Authentication; -using Hestia.Persistence.Contexts; -using MediatR; +using Hestia.Mediator.Infrastructure.Messaging; using Microsoft.AspNetCore.Identity; -using Microsoft.Extensions.Logging; namespace Hestia.Persistence.Handlers.User.Queries.ValidateUserLogin; -internal class ValidateUserLoginQueryHandler(HestiaContext context, UserManager userManager, ILogger logger) : IRequestHandler +internal class ValidateUserLoginQueryHandler(UserManager userManager) : IRequestHandler { public async Task Handle(ValidateUserLoginQuery request, CancellationToken cancellationToken) => await userManager.CheckPasswordAsync(request.User, request.LoginPassword!); diff --git a/Hestia.Persistence/Hestia.Persistence.csproj b/Hestia.Persistence/Hestia.Persistence.csproj index 9047287..dd9c539 100644 --- a/Hestia.Persistence/Hestia.Persistence.csproj +++ b/Hestia.Persistence/Hestia.Persistence.csproj @@ -7,20 +7,19 @@ - - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + diff --git a/Hestia.sln b/Hestia.sln index a9465ef..d44559a 100644 --- a/Hestia.sln +++ b/Hestia.sln @@ -36,6 +36,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hestia.Application.Tests", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hestia.Persistence.Tests", "Hestia.Persistence.Tests\Hestia.Persistence.Tests.csproj", "{7D1330E9-A979-4450-A5DC-F649AE76997C}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hestia.Mediator", "Hestia.Mediator\Hestia.Mediator.csproj", "{0AD8CF15-88C7-4578-B28D-411906F37BA3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hestia.Mapper", "Hestia.Mapper\Hestia.Mapper.csproj", "{C0C93A3B-328E-4D7F-8CE9-2323FC80DF42}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hestia.Application.Models", "Hestia.Application.Models\Hestia.Application.Models.csproj", "{F1504DC2-B9B0-4541-A444-1ACEF29736F3}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -78,6 +84,18 @@ Global {7D1330E9-A979-4450-A5DC-F649AE76997C}.Debug|Any CPU.Build.0 = Debug|Any CPU {7D1330E9-A979-4450-A5DC-F649AE76997C}.Release|Any CPU.ActiveCfg = Release|Any CPU {7D1330E9-A979-4450-A5DC-F649AE76997C}.Release|Any CPU.Build.0 = Release|Any CPU + {0AD8CF15-88C7-4578-B28D-411906F37BA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0AD8CF15-88C7-4578-B28D-411906F37BA3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0AD8CF15-88C7-4578-B28D-411906F37BA3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0AD8CF15-88C7-4578-B28D-411906F37BA3}.Release|Any CPU.Build.0 = Release|Any CPU + {C0C93A3B-328E-4D7F-8CE9-2323FC80DF42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C0C93A3B-328E-4D7F-8CE9-2323FC80DF42}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C0C93A3B-328E-4D7F-8CE9-2323FC80DF42}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C0C93A3B-328E-4D7F-8CE9-2323FC80DF42}.Release|Any CPU.Build.0 = Release|Any CPU + {F1504DC2-B9B0-4541-A444-1ACEF29736F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1504DC2-B9B0-4541-A444-1ACEF29736F3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1504DC2-B9B0-4541-A444-1ACEF29736F3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1504DC2-B9B0-4541-A444-1ACEF29736F3}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -92,6 +110,9 @@ Global {8C67DC6A-C409-4807-A0F9-050CA6241620} = {75F92E7E-EAF5-430E-B7F1-11ACAE8C6D1E} {09840348-019A-44D7-9484-578704D8C579} = {75F92E7E-EAF5-430E-B7F1-11ACAE8C6D1E} {7D1330E9-A979-4450-A5DC-F649AE76997C} = {75F92E7E-EAF5-430E-B7F1-11ACAE8C6D1E} + {0AD8CF15-88C7-4578-B28D-411906F37BA3} = {11A9C530-E38E-468E-B5B7-75C80BEDED80} + {C0C93A3B-328E-4D7F-8CE9-2323FC80DF42} = {35AEA3B0-22A3-4D12-9075-8EA4BE16DA29} + {F1504DC2-B9B0-4541-A444-1ACEF29736F3} = {35AEA3B0-22A3-4D12-9075-8EA4BE16DA29} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {741C93FA-CBE6-4E96-A62E-040CFD186E3C} diff --git a/README.md b/README.md index 688de65..768e0d7 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ This project has the following structure: - Implementation of [**Clean architecture**](https://www.geeksforgeeks.org/complete-guide-to-clean-architecture/) and [**SOLID**](https://en.wikipedia.org/wiki/SOLID) software design principles - [**.NET 9**](https://dotnet.microsoft.com/en-us/download/dotnet/9.0) with [**Entity Framework Core**](https://learn.microsoft.com/en-us/ef/core/) -- [**Command Query Responsibility Segregation (CQRS)**](https://learn.microsoft.com/en-us/azure/architecture/patterns/cqrs) with [**MediatR pattern**](https://en.wikipedia.org/wiki/Mediator_pattern) +- [**Command Query Responsibility Segregation (CQRS)**](https://learn.microsoft.com/en-us/azure/architecture/patterns/cqrs) with [**Mediator pattern**](https://en.wikipedia.org/wiki/Mediator_pattern) - [**JWT Token & Authentication**](https://en.wikipedia.org/wiki/JSON_Web_Token) with [**ASP.NET Core Identity**](https://learn.microsoft.com/en-us/aspnet/core/security/authentication/identity?view=aspnetcore-9.0&tabs=visual-studio) - Global exception middleware handling and data validation using [**Fluent Validation**](https://docs.fluentvalidation.net/en/latest/) library - Standardized API response modeling