From df5530c72620b7cf5ab9a714e5d664b9e4f5cd6d Mon Sep 17 00:00:00 2001 From: alex289 Date: Wed, 22 Mar 2023 19:06:01 +0100 Subject: [PATCH] Full Cleanup --- .../CleanArchitecture.Api.csproj | 22 +++--- .../Controllers/ApiController.cs | 2 +- .../Controllers/UserController.cs | 4 +- CleanArchitecture.Api/Program.cs | 62 +++++++-------- ...CleanArchitecture.Application.Tests.csproj | 14 ++-- .../Queries/QueryHandlerBaseFixture.cs | 2 +- .../Queries/Users/GetAllUsersTestFixture.cs | 44 +++++------ .../Queries/Users/GetUserByIdTestFixture.cs | 22 +++--- .../Users/GetAllUsersQueryHandlerTests.cs | 9 ++- .../Users/GetUserByIdQueryHandlerTests.cs | 10 +-- .../CleanArchitecture.Application.csproj | 4 +- .../Extensions/ServiceCollectionExtension.cs | 6 +- .../Interfaces/IUserService.cs | 2 +- .../Queries/Users/GetAll/GetAllUsersQuery.cs | 2 +- .../Users/GetUserById/GetUserByIdQuery.cs | 2 +- .../GetUserById/GetUserByIdQueryHandler.cs | 6 +- .../Services/UserService.cs | 8 +- .../Users/ChangePasswordViewModel.cs | 2 +- .../ViewModels/Users/LoginUserViewModel.cs | 2 +- .../CleanArchitecture.Domain.Tests.csproj | 12 +-- .../ChangePasswordCommandHandlerTests.cs | 2 +- .../ChangePasswordCommandTestFixture.cs | 10 +-- .../ChangePasswordCommandValidationTests.cs | 41 +++++----- .../CreateUserCommandHandlerTests.cs | 12 +-- .../CreateUserCommandTestFixture.cs | 12 +-- .../CreateUserCommandValidationTests.cs | 76 ++++++++++--------- .../DeleteUserCommandHandlerTests.cs | 12 +-- .../DeleteUserCommandTestFixture.cs | 10 +-- .../DeleteUserCommandValidationTests.cs | 18 +++-- .../LoginUser/LoginUserCommandHandlerTests.cs | 2 +- .../LoginUser/LoginUserCommandTestFixture.cs | 16 ++-- .../LoginUserCommandValidationTests.cs | 60 ++++++++------- .../UpdateUserCommandHandlerTests.cs | 12 +-- .../UpdateUserCommandTestFixture.cs | 12 +-- .../UpdateUserCommandValidationTests.cs | 48 ++++++------ .../CommandHandlerFixtureBase.cs | 12 +-- .../ValidationTestBase.cs | 8 +- CleanArchitecture.Domain/ApiUser.cs | 28 +++---- .../CleanArchitecture.Domain.csproj | 12 +-- .../Commands/CommandBase.cs | 12 +-- .../Commands/CommandHandlerBase.cs | 12 +-- .../ChangePassword/ChangePasswordCommand.cs | 6 +- .../ChangePasswordCommandHandler.cs | 4 +- .../ChangePasswordCommandValidation.cs | 2 +- .../Users/CreateUser/CreateUserCommand.cs | 14 ++-- .../CreateUser/CreateUserCommandHandler.cs | 12 +-- .../CreateUser/CreateUserCommandValidation.cs | 8 +- .../Users/DeleteUser/DeleteUserCommand.cs | 8 +- .../DeleteUser/DeleteUserCommandHandler.cs | 6 +- .../Users/LoginUser/LoginUserCommand.cs | 6 +- .../LoginUser/LoginUserCommandHandler.cs | 22 +++--- .../LoginUser/LoginUserCommandValidation.cs | 2 +- .../Users/UpdateUser/UpdateUserCommand.cs | 12 +-- .../UpdateUser/UpdateUserCommandHandler.cs | 7 +- .../UpdateUser/UpdateUserCommandValidation.cs | 6 +- CleanArchitecture.Domain/Entities/User.cs | 20 ++--- CleanArchitecture.Domain/Enums/UserRole.cs | 2 +- .../Errors/DomainErrorCodes.cs | 2 +- .../EventHandler/UserEventHandler.cs | 8 +- .../Events/User/PasswordChangedEvent.cs | 6 +- .../Events/User/UserCreatedEvent.cs | 4 +- .../Events/User/UserDeletedEvent.cs | 4 +- .../Events/User/UserUpdatedEvent.cs | 4 +- .../Extensions/ServiceCollectionExtension.cs | 4 +- .../Extensions/Validation/CustomValidator.cs | 5 +- CleanArchitecture.Domain/Interfaces/IUser.cs | 5 +- .../Notifications/DomainNotification.cs | 10 +-- .../DomainNotificationHandler.cs | 12 +-- ...anArchitecture.Infrastructure.Tests.csproj | 10 +-- .../DomainNotificationHandlerTests.cs | 20 ++--- .../DomainNotificationTests.cs | 14 ++-- .../UnitOfWorkTests.cs | 10 +-- .../CleanArchitecture.Infrastructure.csproj | 18 ++--- .../Configurations/UserConfiguration.cs | 2 +- .../Database/ApplicationDbContext.cs | 4 +- .../Extensions/DbContextExtension.cs | 2 +- .../Extensions/ServiceCollectionExtensions.cs | 2 +- .../InMemoryBus.cs | 2 +- .../Repositories/BaseRepository.cs | 27 ++++--- .../UnitOfWork.cs | 2 +- .../CleanArchitecture.IntegrationTests.csproj | 20 ++--- .../Controller/UserControllerTests.cs | 55 ++++++++------ ...ctionalTestsServiceCollectionExtensions.cs | 21 ++--- .../Extensions/HttpExtensions.cs | 4 +- .../Fixtures/TestFixtureBase.cs | 6 +- .../Fixtures/UserTestFixture.cs | 4 +- .../CleanArchitectureWebApplicationFactory.cs | 10 +-- .../CleanArchitecture.Proto.csproj | 14 ++-- .../CleanArchitecture.gRPC.Tests.csproj | 14 ++-- .../Fixtures/UserTestsFixture.cs | 40 +++++----- .../CleanArchitecture.gRPC.csproj | 6 +- 91 files changed, 593 insertions(+), 579 deletions(-) diff --git a/CleanArchitecture.Api/CleanArchitecture.Api.csproj b/CleanArchitecture.Api/CleanArchitecture.Api.csproj index 3662059..3ffe10b 100644 --- a/CleanArchitecture.Api/CleanArchitecture.Api.csproj +++ b/CleanArchitecture.Api/CleanArchitecture.Api.csproj @@ -7,22 +7,22 @@ - - + + - all - runtime; build; native; contentfiles; analyzers; buildtransitive + all + runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + + - - - - + + + + diff --git a/CleanArchitecture.Api/Controllers/ApiController.cs b/CleanArchitecture.Api/Controllers/ApiController.cs index a191d22..c092d04 100644 --- a/CleanArchitecture.Api/Controllers/ApiController.cs +++ b/CleanArchitecture.Api/Controllers/ApiController.cs @@ -62,7 +62,7 @@ public class ApiController : ControllerBase { return HttpStatusCode.NotFound; } - + if (_notifications.GetNotifications().Any(n => n.Code == ErrorCodes.InsufficientPermissions)) { return HttpStatusCode.Forbidden; diff --git a/CleanArchitecture.Api/Controllers/UserController.cs b/CleanArchitecture.Api/Controllers/UserController.cs index 3d822b4..7df8108 100644 --- a/CleanArchitecture.Api/Controllers/UserController.cs +++ b/CleanArchitecture.Api/Controllers/UserController.cs @@ -46,7 +46,7 @@ public class UserController : ApiController var user = await _userService.GetUserByUserIdAsync(id, isDeleted); return Response(user); } - + [Authorize] [HttpGet("me")] [SwaggerOperation("Get the current active user")] @@ -56,7 +56,7 @@ public class UserController : ApiController var user = await _userService.GetCurrentUserAsync(); return Response(user); } - + [HttpPost] [SwaggerOperation("Create a new user")] [SwaggerResponse(200, "Request successful", typeof(ResponseMessage))] diff --git a/CleanArchitecture.Api/Program.cs b/CleanArchitecture.Api/Program.cs index 9a76f47..6bfc87b 100644 --- a/CleanArchitecture.Api/Program.cs +++ b/CleanArchitecture.Api/Program.cs @@ -28,37 +28,36 @@ builder.Services.AddSwaggerGen(c => { Title = "CleanArchitecture", Version = "v1", - Description = "A clean architecture API", + Description = "A clean architecture API" }); c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme { Description = "JWT Authorization header using the Bearer scheme. " + - "Use the /auth/azureLogin endpoint to generate a token (use the id_token here), " + - "or create a personal access token in centralhub.", + "Use the /api/v1/user/login endpoint to generate a token", Name = "Authorization", In = ParameterLocation.Header, Type = SecuritySchemeType.Http, Scheme = "bearer" }); - c.AddSecurityRequirement(new OpenApiSecurityRequirement() + c.AddSecurityRequirement(new OpenApiSecurityRequirement + { + { + new OpenApiSecurityScheme { + Reference = new OpenApiReference { - new OpenApiSecurityScheme - { - Reference = new OpenApiReference - { - Type = ReferenceType.SecurityScheme, - Id = "Bearer" - }, - Scheme = "oauth2", - Name = "Bearer", - In = ParameterLocation.Header, - }, - new List() - } - }); + Type = ReferenceType.SecurityScheme, + Id = "Bearer" + }, + Scheme = "oauth2", + Name = "Bearer", + In = ParameterLocation.Header + }, + new List() + } + }); }); builder.Services.AddHealthChecks(); @@ -72,15 +71,9 @@ builder.Services.AddDbContext(options => }); builder.Services.AddAuthentication( - options => - { - options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; - }) + options => { options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer( - jwtOptions => - { - jwtOptions.TokenValidationParameters = CreateTokenValidationParameters(); - }); + jwtOptions => { jwtOptions.TokenValidationParameters = CreateTokenValidationParameters(); }); builder.Services.AddInfrastructure(); builder.Services.AddQueryHandlers(); @@ -94,10 +87,7 @@ builder.Services .Bind(builder.Configuration.GetSection("Auth")) .ValidateOnStart(); -builder.Services.AddMediatR(cfg => -{ - cfg.RegisterServicesFromAssemblies(typeof(Program).Assembly); -}); +builder.Services.AddMediatR(cfg => { cfg.RegisterServicesFromAssemblies(typeof(Program).Assembly); }); var app = builder.Build(); @@ -116,10 +106,10 @@ app.MapControllers(); app.MapHealthChecks("/health"); app.MapGrpcService(); -using (IServiceScope scope = app.Services.CreateScope()) +using (var scope = app.Services.CreateScope()) { var services = scope.ServiceProvider; - ApplicationDbContext appDbContext = services.GetRequiredService(); + var appDbContext = services.GetRequiredService(); appDbContext.EnsureMigrationsApplied(); } @@ -137,8 +127,8 @@ TokenValidationParameters CreateTokenValidationParameters() ValidIssuer = builder.Configuration["Auth:Issuer"], ValidAudience = builder.Configuration["Auth:Audience"], IssuerSigningKey = new SymmetricSecurityKey( - Encoding.UTF8.GetBytes( - builder.Configuration["Auth:Secret"]!)), + Encoding.UTF8.GetBytes( + builder.Configuration["Auth:Secret"]!)), RequireSignedTokens = false }; @@ -146,4 +136,6 @@ TokenValidationParameters CreateTokenValidationParameters() } // Needed for integration tests webapplication factory -public partial class Program { } \ No newline at end of file +public partial class Program +{ +} \ No newline at end of file diff --git a/CleanArchitecture.Application.Tests/CleanArchitecture.Application.Tests.csproj b/CleanArchitecture.Application.Tests/CleanArchitecture.Application.Tests.csproj index 082265f..9af11da 100644 --- a/CleanArchitecture.Application.Tests/CleanArchitecture.Application.Tests.csproj +++ b/CleanArchitecture.Application.Tests/CleanArchitecture.Application.Tests.csproj @@ -8,11 +8,11 @@ - - - - - + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all @@ -24,8 +24,8 @@ - - + + diff --git a/CleanArchitecture.Application.Tests/Fixtures/Queries/QueryHandlerBaseFixture.cs b/CleanArchitecture.Application.Tests/Fixtures/Queries/QueryHandlerBaseFixture.cs index 204e29c..2d995f8 100644 --- a/CleanArchitecture.Application.Tests/Fixtures/Queries/QueryHandlerBaseFixture.cs +++ b/CleanArchitecture.Application.Tests/Fixtures/Queries/QueryHandlerBaseFixture.cs @@ -7,7 +7,7 @@ namespace CleanArchitecture.Application.Tests.Fixtures.Queries; public class QueryHandlerBaseFixture { public Mock Bus { get; } = new(); - + public QueryHandlerBaseFixture VerifyExistingNotification(string key, string errorCode, string message) { Bus.Verify( diff --git a/CleanArchitecture.Application.Tests/Fixtures/Queries/Users/GetAllUsersTestFixture.cs b/CleanArchitecture.Application.Tests/Fixtures/Queries/Users/GetAllUsersTestFixture.cs index e53901c..9dde37d 100644 --- a/CleanArchitecture.Application.Tests/Fixtures/Queries/Users/GetAllUsersTestFixture.cs +++ b/CleanArchitecture.Application.Tests/Fixtures/Queries/Users/GetAllUsersTestFixture.cs @@ -11,30 +11,30 @@ namespace CleanArchitecture.Application.Tests.Fixtures.Queries.Users; public sealed class GetAllUsersTestFixture : QueryHandlerBaseFixture { + public GetAllUsersTestFixture() + { + UserRepository = new Mock(); + + Handler = new GetAllUsersQueryHandler(UserRepository.Object); + } + private Mock UserRepository { get; } public GetAllUsersQueryHandler Handler { get; } public Guid ExistingUserId { get; } = Guid.NewGuid(); - public GetAllUsersTestFixture() - { - UserRepository = new(); - - Handler = new(UserRepository.Object); - } - public void SetupUserAsync() { var user = new Mock(() => - new User( - ExistingUserId, - "max@mustermann.com", - "Max", - "Mustermann", - "Password", - UserRole.User)); + new User( + ExistingUserId, + "max@mustermann.com", + "Max", + "Mustermann", + "Password", + UserRole.User)); var query = new[] { user.Object }.AsQueryable().BuildMock(); - + UserRepository .Setup(x => x.GetAllNoTracking()) .Returns(query); @@ -43,13 +43,13 @@ public sealed class GetAllUsersTestFixture : QueryHandlerBaseFixture public void SetupDeletedUserAsync() { var user = new Mock(() => - new User( - ExistingUserId, - "max@mustermann.com", - "Max", - "Mustermann", - "Password", - UserRole.User)); + new User( + ExistingUserId, + "max@mustermann.com", + "Max", + "Mustermann", + "Password", + UserRole.User)); user.Object.Delete(); diff --git a/CleanArchitecture.Application.Tests/Fixtures/Queries/Users/GetUserByIdTestFixture.cs b/CleanArchitecture.Application.Tests/Fixtures/Queries/Users/GetUserByIdTestFixture.cs index b7c0201..e175faa 100644 --- a/CleanArchitecture.Application.Tests/Fixtures/Queries/Users/GetUserByIdTestFixture.cs +++ b/CleanArchitecture.Application.Tests/Fixtures/Queries/Users/GetUserByIdTestFixture.cs @@ -11,30 +11,30 @@ namespace CleanArchitecture.Application.Tests.Fixtures.Queries.Users; public sealed class GetUserByIdTestFixture : QueryHandlerBaseFixture { + public GetUserByIdTestFixture() + { + UserRepository = new Mock(); + + Handler = new GetUserByIdQueryHandler(UserRepository.Object, Bus.Object); + } + private Mock UserRepository { get; } public GetUserByIdQueryHandler Handler { get; } public Guid ExistingUserId { get; } = Guid.NewGuid(); - public GetUserByIdTestFixture() - { - UserRepository = new(); - - Handler = new(UserRepository.Object, Bus.Object); - } - public void SetupUserAsync() { var user = new Mock(() => new User( - ExistingUserId, - "max@mustermann.com", - "Max", + ExistingUserId, + "max@mustermann.com", + "Max", "Mustermann", "Password", UserRole.User)); var query = new[] { user.Object }.AsQueryable().BuildMock(); - + UserRepository .Setup(x => x.GetAllNoTracking()) .Returns(query); diff --git a/CleanArchitecture.Application.Tests/Queries/Users/GetAllUsersQueryHandlerTests.cs b/CleanArchitecture.Application.Tests/Queries/Users/GetAllUsersQueryHandlerTests.cs index f1ec70e..7f2f3a5 100644 --- a/CleanArchitecture.Application.Tests/Queries/Users/GetAllUsersQueryHandlerTests.cs +++ b/CleanArchitecture.Application.Tests/Queries/Users/GetAllUsersQueryHandlerTests.cs @@ -1,5 +1,6 @@ using System.Linq; using System.Threading.Tasks; +using CleanArchitecture.Application.Queries.Users.GetAll; using CleanArchitecture.Application.Tests.Fixtures.Queries.Users; using FluentAssertions; using Xunit; @@ -16,11 +17,11 @@ public sealed class GetAllUsersQueryHandlerTests _fixture.SetupUserAsync(); var result = await _fixture.Handler.Handle( - new(), + new GetAllUsersQuery(), default); - + _fixture.VerifyNoDomainNotification(); - + result.Should().NotBeNull(); result.Should().ContainSingle(); result.FirstOrDefault()!.Id.Should().Be(_fixture.ExistingUserId); @@ -32,7 +33,7 @@ public sealed class GetAllUsersQueryHandlerTests _fixture.SetupDeletedUserAsync(); var result = await _fixture.Handler.Handle( - new(), + new GetAllUsersQuery(), default); _fixture.VerifyNoDomainNotification(); diff --git a/CleanArchitecture.Application.Tests/Queries/Users/GetUserByIdQueryHandlerTests.cs b/CleanArchitecture.Application.Tests/Queries/Users/GetUserByIdQueryHandlerTests.cs index a65da4c..4b3aec0 100644 --- a/CleanArchitecture.Application.Tests/Queries/Users/GetUserByIdQueryHandlerTests.cs +++ b/CleanArchitecture.Application.Tests/Queries/Users/GetUserByIdQueryHandlerTests.cs @@ -18,15 +18,15 @@ public sealed class GetUserByIdQueryHandlerTests _fixture.SetupUserAsync(); var result = await _fixture.Handler.Handle( - new(_fixture.ExistingUserId, false), + new GetUserByIdQuery(_fixture.ExistingUserId, false), default); - + _fixture.VerifyNoDomainNotification(); result.Should().NotBeNull(); result!.Id.Should().Be(_fixture.ExistingUserId); } - + [Fact] public async Task Should_Raise_Notification_For_No_User() { @@ -36,7 +36,7 @@ public sealed class GetUserByIdQueryHandlerTests var result = await _fixture.Handler.Handle( request, default); - + _fixture.VerifyExistingNotification( nameof(GetUserByIdQuery), ErrorCodes.ObjectNotFound, @@ -51,7 +51,7 @@ public sealed class GetUserByIdQueryHandlerTests _fixture.SetupDeletedUserAsync(); var result = await _fixture.Handler.Handle( - new(_fixture.ExistingUserId, false), + new GetUserByIdQuery(_fixture.ExistingUserId, false), default); _fixture.VerifyExistingNotification( diff --git a/CleanArchitecture.Application/CleanArchitecture.Application.csproj b/CleanArchitecture.Application/CleanArchitecture.Application.csproj index 2f39ab9..2b5f8ef 100644 --- a/CleanArchitecture.Application/CleanArchitecture.Application.csproj +++ b/CleanArchitecture.Application/CleanArchitecture.Application.csproj @@ -6,11 +6,11 @@ - + - + diff --git a/CleanArchitecture.Application/Extensions/ServiceCollectionExtension.cs b/CleanArchitecture.Application/Extensions/ServiceCollectionExtension.cs index beaec9f..784380f 100644 --- a/CleanArchitecture.Application/Extensions/ServiceCollectionExtension.cs +++ b/CleanArchitecture.Application/Extensions/ServiceCollectionExtension.cs @@ -14,15 +14,15 @@ public static class ServiceCollectionExtension public static IServiceCollection AddServices(this IServiceCollection services) { services.AddScoped(); - + return services; } - + public static IServiceCollection AddQueryHandlers(this IServiceCollection services) { services.AddScoped, GetUserByIdQueryHandler>(); services.AddScoped>, GetAllUsersQueryHandler>(); - + return services; } } \ No newline at end of file diff --git a/CleanArchitecture.Application/Interfaces/IUserService.cs b/CleanArchitecture.Application/Interfaces/IUserService.cs index a04dfcb..1fbca3e 100644 --- a/CleanArchitecture.Application/Interfaces/IUserService.cs +++ b/CleanArchitecture.Application/Interfaces/IUserService.cs @@ -1,6 +1,6 @@ -using System.Threading.Tasks; using System; using System.Collections.Generic; +using System.Threading.Tasks; using CleanArchitecture.Application.ViewModels.Users; namespace CleanArchitecture.Application.Interfaces; diff --git a/CleanArchitecture.Application/Queries/Users/GetAll/GetAllUsersQuery.cs b/CleanArchitecture.Application/Queries/Users/GetAll/GetAllUsersQuery.cs index 40e8571..9e59d7f 100644 --- a/CleanArchitecture.Application/Queries/Users/GetAll/GetAllUsersQuery.cs +++ b/CleanArchitecture.Application/Queries/Users/GetAll/GetAllUsersQuery.cs @@ -4,4 +4,4 @@ using MediatR; namespace CleanArchitecture.Application.Queries.Users.GetAll; -public sealed record GetAllUsersQuery : IRequest>; +public sealed record GetAllUsersQuery : IRequest>; \ No newline at end of file diff --git a/CleanArchitecture.Application/Queries/Users/GetUserById/GetUserByIdQuery.cs b/CleanArchitecture.Application/Queries/Users/GetUserById/GetUserByIdQuery.cs index e394336..b4edf1e 100644 --- a/CleanArchitecture.Application/Queries/Users/GetUserById/GetUserByIdQuery.cs +++ b/CleanArchitecture.Application/Queries/Users/GetUserById/GetUserByIdQuery.cs @@ -4,4 +4,4 @@ using MediatR; namespace CleanArchitecture.Application.Queries.Users.GetUserById; -public sealed record GetUserByIdQuery(Guid UserId, bool IsDeleted) : IRequest; +public sealed record GetUserByIdQuery(Guid UserId, bool IsDeleted) : IRequest; \ No newline at end of file diff --git a/CleanArchitecture.Application/Queries/Users/GetUserById/GetUserByIdQueryHandler.cs b/CleanArchitecture.Application/Queries/Users/GetUserById/GetUserByIdQueryHandler.cs index 2c166a8..f057731 100644 --- a/CleanArchitecture.Application/Queries/Users/GetUserById/GetUserByIdQueryHandler.cs +++ b/CleanArchitecture.Application/Queries/Users/GetUserById/GetUserByIdQueryHandler.cs @@ -13,8 +13,8 @@ namespace CleanArchitecture.Application.Queries.Users.GetUserById; public sealed class GetUserByIdQueryHandler : IRequestHandler { - private readonly IUserRepository _userRepository; private readonly IMediatorHandler _bus; + private readonly IUserRepository _userRepository; public GetUserByIdQueryHandler(IUserRepository userRepository, IMediatorHandler bus) { @@ -26,7 +26,7 @@ public sealed class GetUserByIdQueryHandler : { var user = _userRepository .GetAllNoTracking() - .FirstOrDefault(x => + .FirstOrDefault(x => x.Id == request.UserId && x.Deleted == request.IsDeleted); @@ -42,4 +42,4 @@ public sealed class GetUserByIdQueryHandler : return UserViewModel.FromUser(user); } -} +} \ No newline at end of file diff --git a/CleanArchitecture.Application/Services/UserService.cs b/CleanArchitecture.Application/Services/UserService.cs index ee726a5..da20411 100644 --- a/CleanArchitecture.Application/Services/UserService.cs +++ b/CleanArchitecture.Application/Services/UserService.cs @@ -29,7 +29,7 @@ public sealed class UserService : IUserService { return await _bus.QueryAsync(new GetUserByIdQuery(userId, isDeleted)); } - + public async Task GetCurrentUserAsync() { return await _bus.QueryAsync(new GetUserByIdQuery(_user.GetUserId(), false)); @@ -39,7 +39,7 @@ public sealed class UserService : IUserService { return await _bus.QueryAsync(new GetAllUsersQuery()); } - + public async Task CreateUserAsync(CreateUserViewModel user) { var userId = Guid.NewGuid(); @@ -53,7 +53,7 @@ public sealed class UserService : IUserService return userId; } - + public async Task UpdateUserAsync(UpdateUserViewModel user) { await _bus.SendCommandAsync(new UpdateUserCommand( @@ -63,7 +63,7 @@ public sealed class UserService : IUserService user.GivenName, user.Role)); } - + public async Task DeleteUserAsync(Guid userId) { await _bus.SendCommandAsync(new DeleteUserCommand(userId)); diff --git a/CleanArchitecture.Application/ViewModels/Users/ChangePasswordViewModel.cs b/CleanArchitecture.Application/ViewModels/Users/ChangePasswordViewModel.cs index fb02751..bc35cbb 100644 --- a/CleanArchitecture.Application/ViewModels/Users/ChangePasswordViewModel.cs +++ b/CleanArchitecture.Application/ViewModels/Users/ChangePasswordViewModel.cs @@ -1,3 +1,3 @@ namespace CleanArchitecture.Application.ViewModels.Users; -public sealed record ChangePasswordViewModel(string Password, string NewPassword); +public sealed record ChangePasswordViewModel(string Password, string NewPassword); \ No newline at end of file diff --git a/CleanArchitecture.Application/ViewModels/Users/LoginUserViewModel.cs b/CleanArchitecture.Application/ViewModels/Users/LoginUserViewModel.cs index 3883c05..ac5e7de 100644 --- a/CleanArchitecture.Application/ViewModels/Users/LoginUserViewModel.cs +++ b/CleanArchitecture.Application/ViewModels/Users/LoginUserViewModel.cs @@ -1,3 +1,3 @@ namespace CleanArchitecture.Application.ViewModels.Users; -public sealed record LoginUserViewModel(string Email, string Password); +public sealed record LoginUserViewModel(string Email, string Password); \ No newline at end of file diff --git a/CleanArchitecture.Domain.Tests/CleanArchitecture.Domain.Tests.csproj b/CleanArchitecture.Domain.Tests/CleanArchitecture.Domain.Tests.csproj index 72a5f62..dfa6f77 100644 --- a/CleanArchitecture.Domain.Tests/CleanArchitecture.Domain.Tests.csproj +++ b/CleanArchitecture.Domain.Tests/CleanArchitecture.Domain.Tests.csproj @@ -8,11 +8,11 @@ - - - - - + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all @@ -24,7 +24,7 @@ - + diff --git a/CleanArchitecture.Domain.Tests/CommandHandler/User/ChangePassword/ChangePasswordCommandHandlerTests.cs b/CleanArchitecture.Domain.Tests/CommandHandler/User/ChangePassword/ChangePasswordCommandHandlerTests.cs index a6fc1e9..08d16ca 100644 --- a/CleanArchitecture.Domain.Tests/CommandHandler/User/ChangePassword/ChangePasswordCommandHandlerTests.cs +++ b/CleanArchitecture.Domain.Tests/CommandHandler/User/ChangePassword/ChangePasswordCommandHandlerTests.cs @@ -60,4 +60,4 @@ public sealed class ChangePasswordCommandHandlerTests DomainErrorCodes.UserPasswordIncorrect, "The password is incorrect"); } -} +} \ No newline at end of file diff --git a/CleanArchitecture.Domain.Tests/CommandHandler/User/ChangePassword/ChangePasswordCommandTestFixture.cs b/CleanArchitecture.Domain.Tests/CommandHandler/User/ChangePassword/ChangePasswordCommandTestFixture.cs index 6162a21..77e8e55 100644 --- a/CleanArchitecture.Domain.Tests/CommandHandler/User/ChangePassword/ChangePasswordCommandTestFixture.cs +++ b/CleanArchitecture.Domain.Tests/CommandHandler/User/ChangePassword/ChangePasswordCommandTestFixture.cs @@ -9,14 +9,11 @@ namespace CleanArchitecture.Domain.Tests.CommandHandler.User.ChangePassword; public sealed class ChangePasswordCommandTestFixture : CommandHandlerFixtureBase { - public ChangePasswordCommandHandler CommandHandler { get; set; } - public Mock UserRepository { get; set; } - public ChangePasswordCommandTestFixture() { UserRepository = new Mock(); - CommandHandler = new( + CommandHandler = new ChangePasswordCommandHandler( Bus.Object, UnitOfWork.Object, NotificationHandler.Object, @@ -24,6 +21,9 @@ public sealed class ChangePasswordCommandTestFixture : CommandHandlerFixtureBase User.Object); } + public ChangePasswordCommandHandler CommandHandler { get; set; } + public Mock UserRepository { get; set; } + public Entities.User SetupUser() { var user = new Entities.User( @@ -49,4 +49,4 @@ public sealed class ChangePasswordCommandTestFixture : CommandHandlerFixtureBase User.Setup(x => x.GetUserId()).Returns(id); return id; } -} +} \ No newline at end of file diff --git a/CleanArchitecture.Domain.Tests/CommandHandler/User/ChangePassword/ChangePasswordCommandValidationTests.cs b/CleanArchitecture.Domain.Tests/CommandHandler/User/ChangePassword/ChangePasswordCommandValidationTests.cs index d68220c..3b0339e 100644 --- a/CleanArchitecture.Domain.Tests/CommandHandler/User/ChangePassword/ChangePasswordCommandValidationTests.cs +++ b/CleanArchitecture.Domain.Tests/CommandHandler/User/ChangePassword/ChangePasswordCommandValidationTests.cs @@ -12,15 +12,15 @@ public sealed class ChangePasswordCommandValidationTests : public ChangePasswordCommandValidationTests() : base(new ChangePasswordCommandValidation()) { } - + [Fact] public void Should_Be_Valid() { var command = CreateTestCommand(); - + ShouldBeValid(command); } - + [Fact] public void Should_Be_Invalid_For_Empty_Password() { @@ -35,60 +35,63 @@ public sealed class ChangePasswordCommandValidationTests : DomainErrorCodes.UserUppercaseLetterPassword, DomainErrorCodes.UserShortPassword }; - + ShouldHaveExpectedErrors(command, errors.ToArray()); } - + [Fact] public void Should_Be_Invalid_For_Missing_Special_Character() { var command = CreateTestCommand("z8tnayvd5FNLU9AQm"); - + ShouldHaveSingleError(command, DomainErrorCodes.UserSpecialCharPassword); } - + [Fact] public void Should_Be_Invalid_For_Missing_Number() { var command = CreateTestCommand("z]tnayvdFNLU:]AQm"); - + ShouldHaveSingleError(command, DomainErrorCodes.UserNumberPassword); } - + [Fact] public void Should_Be_Invalid_For_Missing_Lowercase_Character() { var command = CreateTestCommand("Z8]TNAYVDFNLU:]AQM"); - + ShouldHaveSingleError(command, DomainErrorCodes.UserLowercaseLetterPassword); } - + [Fact] public void Should_Be_Invalid_For_Missing_Uppercase_Character() { var command = CreateTestCommand("z8]tnayvd5fnlu9:]aqm"); - + ShouldHaveSingleError(command, DomainErrorCodes.UserUppercaseLetterPassword); } - + [Fact] public void Should_Be_Invalid_For_Password_Too_Short() { var command = CreateTestCommand("zA6{"); - + ShouldHaveSingleError(command, DomainErrorCodes.UserShortPassword); } - + [Fact] public void Should_Be_Invalid_For_Password_Too_Long() { var command = CreateTestCommand(string.Concat(Enumerable.Repeat("zA6{", 12), 12)); - + ShouldHaveSingleError(command, DomainErrorCodes.UserLongPassword); } private ChangePasswordCommand CreateTestCommand( - string? password = null, string? newPassword = null) => new( - password ?? "z8]tnayvd5FNLU9:]AQm", + string? password = null, string? newPassword = null) + { + return new( + password ?? "z8]tnayvd5FNLU9:]AQm", newPassword ?? "z8]tnayvd5FNLU9:]AQw"); -} + } +} \ No newline at end of file diff --git a/CleanArchitecture.Domain.Tests/CommandHandler/User/CreateUser/CreateUserCommandHandlerTests.cs b/CleanArchitecture.Domain.Tests/CommandHandler/User/CreateUser/CreateUserCommandHandlerTests.cs index 50eaa79..9319274 100644 --- a/CleanArchitecture.Domain.Tests/CommandHandler/User/CreateUser/CreateUserCommandHandlerTests.cs +++ b/CleanArchitecture.Domain.Tests/CommandHandler/User/CreateUser/CreateUserCommandHandlerTests.cs @@ -9,19 +9,19 @@ namespace CleanArchitecture.Domain.Tests.CommandHandler.User.CreateUser; public sealed class CreateUserCommandHandlerTests { private readonly CreateUserCommandTestFixture _fixture = new(); - + [Fact] public void Should_Create_User() { _fixture.SetupUser(); - + var command = new CreateUserCommand( Guid.NewGuid(), "test@email.com", "Test", "Email", "Po=PF]PC6t.?8?ks)A6W"); - + _fixture.CommandHandler.Handle(command, default).Wait(); _fixture @@ -29,19 +29,19 @@ public sealed class CreateUserCommandHandlerTests .VerifyCommit() .VerifyRaisedEvent(x => x.UserId == command.UserId); } - + [Fact] public void Should_Not_Create_Already_Existing_User() { var user = _fixture.SetupUser(); - + var command = new CreateUserCommand( user.Id, "test@email.com", "Test", "Email", "Po=PF]PC6t.?8?ks)A6W"); - + _fixture.CommandHandler.Handle(command, default).Wait(); _fixture diff --git a/CleanArchitecture.Domain.Tests/CommandHandler/User/CreateUser/CreateUserCommandTestFixture.cs b/CleanArchitecture.Domain.Tests/CommandHandler/User/CreateUser/CreateUserCommandTestFixture.cs index c7ba05a..f6879b3 100644 --- a/CleanArchitecture.Domain.Tests/CommandHandler/User/CreateUser/CreateUserCommandTestFixture.cs +++ b/CleanArchitecture.Domain.Tests/CommandHandler/User/CreateUser/CreateUserCommandTestFixture.cs @@ -8,20 +8,20 @@ namespace CleanArchitecture.Domain.Tests.CommandHandler.User.CreateUser; public sealed class CreateUserCommandTestFixture : CommandHandlerFixtureBase { - public CreateUserCommandHandler CommandHandler { get; } - private Mock UserRepository { get; } - public CreateUserCommandTestFixture() { UserRepository = new Mock(); - - CommandHandler = new( + + CommandHandler = new CreateUserCommandHandler( Bus.Object, UnitOfWork.Object, NotificationHandler.Object, UserRepository.Object); } - + + public CreateUserCommandHandler CommandHandler { get; } + private Mock UserRepository { get; } + public Entities.User SetupUser() { var user = new Entities.User( diff --git a/CleanArchitecture.Domain.Tests/CommandHandler/User/CreateUser/CreateUserCommandValidationTests.cs b/CleanArchitecture.Domain.Tests/CommandHandler/User/CreateUser/CreateUserCommandValidationTests.cs index 61a00ad..f3bbe6d 100644 --- a/CleanArchitecture.Domain.Tests/CommandHandler/User/CreateUser/CreateUserCommandValidationTests.cs +++ b/CleanArchitecture.Domain.Tests/CommandHandler/User/CreateUser/CreateUserCommandValidationTests.cs @@ -13,103 +13,103 @@ public sealed class CreateUserCommandValidationTests : public CreateUserCommandValidationTests() : base(new CreateUserCommandValidation()) { } - + [Fact] public void Should_Be_Valid() { var command = CreateTestCommand(); - + ShouldBeValid(command); } - + [Fact] public void Should_Be_Invalid_For_Empty_User_Id() { - var command = CreateTestCommand(userId: Guid.Empty); - + var command = CreateTestCommand(Guid.Empty); + ShouldHaveSingleError( - command, + command, DomainErrorCodes.UserEmptyId, "User id may not be empty"); } - + [Fact] public void Should_Be_Invalid_For_Empty_Email() { var command = CreateTestCommand(email: string.Empty); - + ShouldHaveSingleError( command, DomainErrorCodes.UserInvalidEmail, "Email is not a valid email address"); } - + [Fact] public void Should_Be_Invalid_For_Invalid_Email() { var command = CreateTestCommand(email: "not a email"); - + ShouldHaveSingleError( command, DomainErrorCodes.UserInvalidEmail, "Email is not a valid email address"); } - + [Fact] public void Should_Be_Invalid_For_Email_Exceeds_Max_Length() { var command = CreateTestCommand(email: new string('a', 320) + "@test.com"); - + ShouldHaveSingleError( command, DomainErrorCodes.UserEmailExceedsMaxLength, "Email may not be longer than 320 characters"); } - + [Fact] public void Should_Be_Invalid_For_Empty_Surname() { var command = CreateTestCommand(surName: ""); - + ShouldHaveSingleError( command, DomainErrorCodes.UserEmptySurname, "Surname may not be empty"); } - + [Fact] public void Should_Be_Invalid_For_Surname_Exceeds_Max_Length() { var command = CreateTestCommand(surName: new string('a', 101)); - + ShouldHaveSingleError( command, DomainErrorCodes.UserSurnameExceedsMaxLength, "Surname may not be longer than 100 characters"); } - + [Fact] public void Should_Be_Invalid_For_Empty_Given_Name() { var command = CreateTestCommand(givenName: ""); - + ShouldHaveSingleError( command, DomainErrorCodes.UserEmptyGivenName, "Given name may not be empty"); } - + [Fact] public void Should_Be_Invalid_For_Given_Name_Exceeds_Max_Length() { var command = CreateTestCommand(givenName: new string('a', 101)); - + ShouldHaveSingleError( command, DomainErrorCodes.UserGivenNameExceedsMaxLength, "Given name may not be longer than 100 characters"); } - + [Fact] public void Should_Be_Invalid_For_Empty_Password() { @@ -124,68 +124,70 @@ public sealed class CreateUserCommandValidationTests : DomainErrorCodes.UserUppercaseLetterPassword, DomainErrorCodes.UserShortPassword }; - + ShouldHaveExpectedErrors(command, errors.ToArray()); } - + [Fact] public void Should_Be_Invalid_For_Missing_Special_Character() { var command = CreateTestCommand(password: "z8tnayvd5FNLU9AQm"); - + ShouldHaveSingleError(command, DomainErrorCodes.UserSpecialCharPassword); } - + [Fact] public void Should_Be_Invalid_For_Missing_Number() { var command = CreateTestCommand(password: "z]tnayvdFNLU:]AQm"); - + ShouldHaveSingleError(command, DomainErrorCodes.UserNumberPassword); } - + [Fact] public void Should_Be_Invalid_For_Missing_Lowercase_Character() { var command = CreateTestCommand(password: "Z8]TNAYVDFNLU:]AQM"); - + ShouldHaveSingleError(command, DomainErrorCodes.UserLowercaseLetterPassword); } - + [Fact] public void Should_Be_Invalid_For_Missing_Uppercase_Character() { var command = CreateTestCommand(password: "z8]tnayvd5fnlu9:]aqm"); - + ShouldHaveSingleError(command, DomainErrorCodes.UserUppercaseLetterPassword); } - + [Fact] public void Should_Be_Invalid_For_Password_Too_Short() { var command = CreateTestCommand(password: "zA6{"); - + ShouldHaveSingleError(command, DomainErrorCodes.UserShortPassword); } - + [Fact] public void Should_Be_Invalid_For_Password_Too_Long() { var command = CreateTestCommand(password: string.Concat(Enumerable.Repeat("zA6{", 12), 12)); - + ShouldHaveSingleError(command, DomainErrorCodes.UserLongPassword); } - + private CreateUserCommand CreateTestCommand( Guid? userId = null, string? email = null, string? surName = null, string? givenName = null, - string? password = null) => - new ( + string? password = null) + { + return new( userId ?? Guid.NewGuid(), email ?? "test@email.com", surName ?? "test", givenName ?? "email", password ?? "Po=PF]PC6t.?8?ks)A6W"); + } } \ No newline at end of file diff --git a/CleanArchitecture.Domain.Tests/CommandHandler/User/DeleteUser/DeleteUserCommandHandlerTests.cs b/CleanArchitecture.Domain.Tests/CommandHandler/User/DeleteUser/DeleteUserCommandHandlerTests.cs index dc23bd6..fabfd29 100644 --- a/CleanArchitecture.Domain.Tests/CommandHandler/User/DeleteUser/DeleteUserCommandHandlerTests.cs +++ b/CleanArchitecture.Domain.Tests/CommandHandler/User/DeleteUser/DeleteUserCommandHandlerTests.cs @@ -9,14 +9,14 @@ namespace CleanArchitecture.Domain.Tests.CommandHandler.User.DeleteUser; public sealed class DeleteUserCommandHandlerTests { private readonly DeleteUserCommandTestFixture _fixture = new(); - + [Fact] public void Should_Delete_User() { var user = _fixture.SetupUser(); - + var command = new DeleteUserCommand(user.Id); - + _fixture.CommandHandler.Handle(command, default).Wait(); _fixture @@ -24,14 +24,14 @@ public sealed class DeleteUserCommandHandlerTests .VerifyCommit() .VerifyRaisedEvent(x => x.UserId == user.Id); } - + [Fact] public void Should_Not_Delete_Non_Existing_User() { _fixture.SetupUser(); - + var command = new DeleteUserCommand(Guid.NewGuid()); - + _fixture.CommandHandler.Handle(command, default).Wait(); _fixture diff --git a/CleanArchitecture.Domain.Tests/CommandHandler/User/DeleteUser/DeleteUserCommandTestFixture.cs b/CleanArchitecture.Domain.Tests/CommandHandler/User/DeleteUser/DeleteUserCommandTestFixture.cs index 149480d..aaf2e66 100644 --- a/CleanArchitecture.Domain.Tests/CommandHandler/User/DeleteUser/DeleteUserCommandTestFixture.cs +++ b/CleanArchitecture.Domain.Tests/CommandHandler/User/DeleteUser/DeleteUserCommandTestFixture.cs @@ -8,14 +8,11 @@ namespace CleanArchitecture.Domain.Tests.CommandHandler.User.DeleteUser; public sealed class DeleteUserCommandTestFixture : CommandHandlerFixtureBase { - public DeleteUserCommandHandler CommandHandler { get; } - private Mock UserRepository { get; } - public DeleteUserCommandTestFixture() { UserRepository = new Mock(); - - CommandHandler = new ( + + CommandHandler = new DeleteUserCommandHandler( Bus.Object, UnitOfWork.Object, NotificationHandler.Object, @@ -23,6 +20,9 @@ public sealed class DeleteUserCommandTestFixture : CommandHandlerFixtureBase User.Object); } + public DeleteUserCommandHandler CommandHandler { get; } + private Mock UserRepository { get; } + public Entities.User SetupUser() { var user = new Entities.User( diff --git a/CleanArchitecture.Domain.Tests/CommandHandler/User/DeleteUser/DeleteUserCommandValidationTests.cs b/CleanArchitecture.Domain.Tests/CommandHandler/User/DeleteUser/DeleteUserCommandValidationTests.cs index 50106e5..4ace62f 100644 --- a/CleanArchitecture.Domain.Tests/CommandHandler/User/DeleteUser/DeleteUserCommandValidationTests.cs +++ b/CleanArchitecture.Domain.Tests/CommandHandler/User/DeleteUser/DeleteUserCommandValidationTests.cs @@ -16,21 +16,23 @@ public sealed class DeleteUserCommandValidationTests : public void Should_Be_Valid() { var command = CreateTestCommand(); - + ShouldBeValid(command); } - + [Fact] public void Should_Be_Invalid_For_Empty_User_Id() { - var command = CreateTestCommand(userId: Guid.Empty); - + var command = CreateTestCommand(Guid.Empty); + ShouldHaveSingleError( - command, + command, DomainErrorCodes.UserEmptyId, "User id may not be empty"); } - - private DeleteUserCommand CreateTestCommand(Guid? userId = null) => - new (userId ?? Guid.NewGuid()); + + private DeleteUserCommand CreateTestCommand(Guid? userId = null) + { + return new(userId ?? Guid.NewGuid()); + } } \ No newline at end of file diff --git a/CleanArchitecture.Domain.Tests/CommandHandler/User/LoginUser/LoginUserCommandHandlerTests.cs b/CleanArchitecture.Domain.Tests/CommandHandler/User/LoginUser/LoginUserCommandHandlerTests.cs index 700da13..6137b67 100644 --- a/CleanArchitecture.Domain.Tests/CommandHandler/User/LoginUser/LoginUserCommandHandlerTests.cs +++ b/CleanArchitecture.Domain.Tests/CommandHandler/User/LoginUser/LoginUserCommandHandlerTests.cs @@ -79,4 +79,4 @@ public sealed class LoginUserCommandHandlerTests token.Should().BeEmpty(); } -} +} \ No newline at end of file diff --git a/CleanArchitecture.Domain.Tests/CommandHandler/User/LoginUser/LoginUserCommandTestFixture.cs b/CleanArchitecture.Domain.Tests/CommandHandler/User/LoginUser/LoginUserCommandTestFixture.cs index 8010194..71ee339 100644 --- a/CleanArchitecture.Domain.Tests/CommandHandler/User/LoginUser/LoginUserCommandTestFixture.cs +++ b/CleanArchitecture.Domain.Tests/CommandHandler/User/LoginUser/LoginUserCommandTestFixture.cs @@ -1,20 +1,16 @@ -using CleanArchitecture.Domain.Commands.Users.LoginUser; +using System; +using CleanArchitecture.Domain.Commands.Users.LoginUser; using CleanArchitecture.Domain.Enums; using CleanArchitecture.Domain.Interfaces.Repositories; using CleanArchitecture.Domain.Settings; using Microsoft.Extensions.Options; using Moq; -using System; using BC = BCrypt.Net.BCrypt; namespace CleanArchitecture.Domain.Tests.CommandHandler.User.LoginUser; public sealed class LoginUserCommandTestFixture : CommandHandlerFixtureBase { - public LoginUserCommandHandler CommandHandler { get; set; } - public Mock UserRepository { get; set; } - public IOptions TokenSettings { get; set; } - public LoginUserCommandTestFixture() { UserRepository = new Mock(); @@ -26,7 +22,7 @@ public sealed class LoginUserCommandTestFixture : CommandHandlerFixtureBase Secret = "asjdlkasjd87439284)@#(*" }); - CommandHandler = new( + CommandHandler = new LoginUserCommandHandler( Bus.Object, UnitOfWork.Object, NotificationHandler.Object, @@ -34,6 +30,10 @@ public sealed class LoginUserCommandTestFixture : CommandHandlerFixtureBase TokenSettings); } + public LoginUserCommandHandler CommandHandler { get; set; } + public Mock UserRepository { get; set; } + public IOptions TokenSettings { get; set; } + public Entities.User SetupUser() { var user = new Entities.User( @@ -52,4 +52,4 @@ public sealed class LoginUserCommandTestFixture : CommandHandlerFixtureBase return user; } -} +} \ No newline at end of file diff --git a/CleanArchitecture.Domain.Tests/CommandHandler/User/LoginUser/LoginUserCommandValidationTests.cs b/CleanArchitecture.Domain.Tests/CommandHandler/User/LoginUser/LoginUserCommandValidationTests.cs index 223f711..96fa7c3 100644 --- a/CleanArchitecture.Domain.Tests/CommandHandler/User/LoginUser/LoginUserCommandValidationTests.cs +++ b/CleanArchitecture.Domain.Tests/CommandHandler/User/LoginUser/LoginUserCommandValidationTests.cs @@ -12,48 +12,48 @@ public sealed class LoginUserCommandValidationTests : public LoginUserCommandValidationTests() : base(new LoginUserCommandValidation()) { } - + [Fact] public void Should_Be_Valid() { var command = CreateTestCommand(); - + ShouldBeValid(command); } - + [Fact] public void Should_Be_Invalid_For_Empty_Email() { - var command = CreateTestCommand(email: string.Empty); - + var command = CreateTestCommand(string.Empty); + ShouldHaveSingleError( command, DomainErrorCodes.UserInvalidEmail, "Email is not a valid email address"); } - + [Fact] public void Should_Be_Invalid_For_Invalid_Email() { - var command = CreateTestCommand(email: "not a email"); - + var command = CreateTestCommand("not a email"); + ShouldHaveSingleError( command, DomainErrorCodes.UserInvalidEmail, "Email is not a valid email address"); } - + [Fact] public void Should_Be_Invalid_For_Email_Exceeds_Max_Length() { - var command = CreateTestCommand(email: new string('a', 320) + "@test.com"); - + var command = CreateTestCommand(new string('a', 320) + "@test.com"); + ShouldHaveSingleError( command, DomainErrorCodes.UserEmailExceedsMaxLength, "Email may not be longer than 320 characters"); } - + [Fact] public void Should_Be_Invalid_For_Empty_Password() { @@ -68,62 +68,64 @@ public sealed class LoginUserCommandValidationTests : DomainErrorCodes.UserUppercaseLetterPassword, DomainErrorCodes.UserShortPassword }; - + ShouldHaveExpectedErrors(command, errors.ToArray()); } - + [Fact] public void Should_Be_Invalid_For_Missing_Special_Character() { var command = CreateTestCommand(password: "z8tnayvd5FNLU9AQm"); - + ShouldHaveSingleError(command, DomainErrorCodes.UserSpecialCharPassword); } - + [Fact] public void Should_Be_Invalid_For_Missing_Number() { var command = CreateTestCommand(password: "z]tnayvdFNLU:]AQm"); - + ShouldHaveSingleError(command, DomainErrorCodes.UserNumberPassword); } - + [Fact] public void Should_Be_Invalid_For_Missing_Lowercase_Character() { var command = CreateTestCommand(password: "Z8]TNAYVDFNLU:]AQM"); - + ShouldHaveSingleError(command, DomainErrorCodes.UserLowercaseLetterPassword); } - + [Fact] public void Should_Be_Invalid_For_Missing_Uppercase_Character() { var command = CreateTestCommand(password: "z8]tnayvd5fnlu9:]aqm"); - + ShouldHaveSingleError(command, DomainErrorCodes.UserUppercaseLetterPassword); } - + [Fact] public void Should_Be_Invalid_For_Password_Too_Short() { var command = CreateTestCommand(password: "zA6{"); - + ShouldHaveSingleError(command, DomainErrorCodes.UserShortPassword); } - + [Fact] public void Should_Be_Invalid_For_Password_Too_Long() { var command = CreateTestCommand(password: string.Concat(Enumerable.Repeat("zA6{", 12), 12)); - + ShouldHaveSingleError(command, DomainErrorCodes.UserLongPassword); } - + private LoginUserCommand CreateTestCommand( string? email = null, - string? password = null) => - new ( + string? password = null) + { + return new( email ?? "test@email.com", password ?? "Po=PF]PC6t.?8?ks)A6W"); -} + } +} \ No newline at end of file diff --git a/CleanArchitecture.Domain.Tests/CommandHandler/User/UpdateUser/UpdateUserCommandHandlerTests.cs b/CleanArchitecture.Domain.Tests/CommandHandler/User/UpdateUser/UpdateUserCommandHandlerTests.cs index 3fcb757..98a13df 100644 --- a/CleanArchitecture.Domain.Tests/CommandHandler/User/UpdateUser/UpdateUserCommandHandlerTests.cs +++ b/CleanArchitecture.Domain.Tests/CommandHandler/User/UpdateUser/UpdateUserCommandHandlerTests.cs @@ -11,19 +11,19 @@ namespace CleanArchitecture.Domain.Tests.CommandHandler.User.UpdateUser; public sealed class UpdateUserCommandHandlerTests { private readonly UpdateUserCommandTestFixture _fixture = new(); - + [Fact] public async Task Should_Update_User() { var user = _fixture.SetupUser(); - + var command = new UpdateUserCommand( user.Id, "test@email.com", "Test", "Email", UserRole.User); - + await _fixture.CommandHandler.Handle(command, default); _fixture @@ -31,19 +31,19 @@ public sealed class UpdateUserCommandHandlerTests .VerifyCommit() .VerifyRaisedEvent(x => x.UserId == command.UserId); } - + [Fact] public async Task Should_Not_Update_Non_Existing_User() { _fixture.SetupUser(); - + var command = new UpdateUserCommand( Guid.NewGuid(), "test@email.com", "Test", "Email", UserRole.User); - + await _fixture.CommandHandler.Handle(command, default); _fixture diff --git a/CleanArchitecture.Domain.Tests/CommandHandler/User/UpdateUser/UpdateUserCommandTestFixture.cs b/CleanArchitecture.Domain.Tests/CommandHandler/User/UpdateUser/UpdateUserCommandTestFixture.cs index bb4e581..69bb3ca 100644 --- a/CleanArchitecture.Domain.Tests/CommandHandler/User/UpdateUser/UpdateUserCommandTestFixture.cs +++ b/CleanArchitecture.Domain.Tests/CommandHandler/User/UpdateUser/UpdateUserCommandTestFixture.cs @@ -8,21 +8,21 @@ namespace CleanArchitecture.Domain.Tests.CommandHandler.User.UpdateUser; public sealed class UpdateUserCommandTestFixture : CommandHandlerFixtureBase { - public UpdateUserCommandHandler CommandHandler { get; } - private Mock UserRepository { get; } - public UpdateUserCommandTestFixture() { UserRepository = new Mock(); - - CommandHandler = new( + + CommandHandler = new UpdateUserCommandHandler( Bus.Object, UnitOfWork.Object, NotificationHandler.Object, UserRepository.Object, User.Object); } - + + public UpdateUserCommandHandler CommandHandler { get; } + private Mock UserRepository { get; } + public Entities.User SetupUser() { var user = new Entities.User( diff --git a/CleanArchitecture.Domain.Tests/CommandHandler/User/UpdateUser/UpdateUserCommandValidationTests.cs b/CleanArchitecture.Domain.Tests/CommandHandler/User/UpdateUser/UpdateUserCommandValidationTests.cs index dcf9d11..2fe4e21 100644 --- a/CleanArchitecture.Domain.Tests/CommandHandler/User/UpdateUser/UpdateUserCommandValidationTests.cs +++ b/CleanArchitecture.Domain.Tests/CommandHandler/User/UpdateUser/UpdateUserCommandValidationTests.cs @@ -12,113 +12,115 @@ public sealed class UpdateUserCommandValidationTests : public UpdateUserCommandValidationTests() : base(new UpdateUserCommandValidation()) { } - + [Fact] public void Should_Be_Valid() { var command = CreateTestCommand(); - + ShouldBeValid(command); } - + [Fact] public void Should_Be_Invalid_For_Empty_User_Id() { - var command = CreateTestCommand(userId: Guid.Empty); - + var command = CreateTestCommand(Guid.Empty); + ShouldHaveSingleError( - command, + command, DomainErrorCodes.UserEmptyId, "User id may not be empty"); } - + [Fact] public void Should_Be_Invalid_For_Empty_Email() { var command = CreateTestCommand(email: string.Empty); - + ShouldHaveSingleError( command, DomainErrorCodes.UserInvalidEmail, "Email is not a valid email address"); } - + [Fact] public void Should_Be_Invalid_For_Invalid_Email() { var command = CreateTestCommand(email: "not a email"); - + ShouldHaveSingleError( command, DomainErrorCodes.UserInvalidEmail, "Email is not a valid email address"); } - + [Fact] public void Should_Be_Invalid_For_Email_Exceeds_Max_Length() { var command = CreateTestCommand(email: new string('a', 320) + "@test.com"); - + ShouldHaveSingleError( command, DomainErrorCodes.UserEmailExceedsMaxLength, "Email may not be longer than 320 characters"); } - + [Fact] public void Should_Be_Invalid_For_Empty_Surname() { var command = CreateTestCommand(surName: ""); - + ShouldHaveSingleError( command, DomainErrorCodes.UserEmptySurname, "Surname may not be empty"); } - + [Fact] public void Should_Be_Invalid_For_Surname_Exceeds_Max_Length() { var command = CreateTestCommand(surName: new string('a', 101)); - + ShouldHaveSingleError( command, DomainErrorCodes.UserSurnameExceedsMaxLength, "Surname may not be longer than 100 characters"); } - + [Fact] public void Should_Be_Invalid_For_Empty_Given_Name() { var command = CreateTestCommand(givenName: ""); - + ShouldHaveSingleError( command, DomainErrorCodes.UserEmptyGivenName, "Given name may not be empty"); } - + [Fact] public void Should_Be_Invalid_For_Given_Name_Exceeds_Max_Length() { var command = CreateTestCommand(givenName: new string('a', 101)); - + ShouldHaveSingleError( command, DomainErrorCodes.UserGivenNameExceedsMaxLength, "Given name may not be longer than 100 characters"); } - + private static UpdateUserCommand CreateTestCommand( Guid? userId = null, string? email = null, string? surName = null, string? givenName = null, - UserRole? role = null) => - new ( + UserRole? role = null) + { + return new( userId ?? Guid.NewGuid(), email ?? "test@email.com", surName ?? "test", givenName ?? "email", role ?? UserRole.User); + } } \ No newline at end of file diff --git a/CleanArchitecture.Domain.Tests/CommandHandlerFixtureBase.cs b/CleanArchitecture.Domain.Tests/CommandHandlerFixtureBase.cs index a8e464c..ce8a493 100644 --- a/CleanArchitecture.Domain.Tests/CommandHandlerFixtureBase.cs +++ b/CleanArchitecture.Domain.Tests/CommandHandlerFixtureBase.cs @@ -9,24 +9,24 @@ namespace CleanArchitecture.Domain.Tests; public class CommandHandlerFixtureBase { - protected Mock Bus { get; } - protected Mock UnitOfWork { get; } - protected Mock NotificationHandler { get; } - protected Mock User { get; } - protected CommandHandlerFixtureBase() { Bus = new Mock(); UnitOfWork = new Mock(); NotificationHandler = new Mock(); User = new Mock(); - + User.Setup(x => x.GetUserId()).Returns(Guid.NewGuid()); User.Setup(x => x.GetUserRole()).Returns(UserRole.Admin); UnitOfWork.Setup(unit => unit.CommitAsync()).ReturnsAsync(true); } + protected Mock Bus { get; } + protected Mock UnitOfWork { get; } + protected Mock NotificationHandler { get; } + protected Mock User { get; } + public CommandHandlerFixtureBase VerifyExistingNotification(string errorCode, string message) { Bus.Verify( diff --git a/CleanArchitecture.Domain.Tests/ValidationTestBase.cs b/CleanArchitecture.Domain.Tests/ValidationTestBase.cs index cbb3692..37391ee 100644 --- a/CleanArchitecture.Domain.Tests/ValidationTestBase.cs +++ b/CleanArchitecture.Domain.Tests/ValidationTestBase.cs @@ -8,7 +8,7 @@ namespace CleanArchitecture.Domain.Tests; public class ValidationTestBase where TCommand : CommandBase - where TValidation: AbstractValidator + where TValidation : AbstractValidator { private readonly TValidation _validation; @@ -54,7 +54,7 @@ public class ValidationTestBase } protected void ShouldHaveExpectedErrors( - TCommand command, + TCommand command, params KeyValuePair[] expectedErrors) { var result = _validation.Validate(command); @@ -70,9 +70,9 @@ public class ValidationTestBase .Be(1); } } - + protected void ShouldHaveExpectedErrors( - TCommand command, + TCommand command, params string[] expectedErrors) { var result = _validation.Validate(command); diff --git a/CleanArchitecture.Domain/ApiUser.cs b/CleanArchitecture.Domain/ApiUser.cs index c991c79..15912af 100644 --- a/CleanArchitecture.Domain/ApiUser.cs +++ b/CleanArchitecture.Domain/ApiUser.cs @@ -29,19 +29,6 @@ public sealed class ApiUser : IUser throw new ArgumentException("Could not parse user id to guid"); } - public string GetUserEmail() - { - var claim = _httpContextAccessor.HttpContext?.User.Claims - .FirstOrDefault(x => string.Equals(x.Type, ClaimTypes.Email)); - - if (!string.IsNullOrWhiteSpace(claim?.Value)) - { - return claim?.Value!; - } - - throw new ArgumentException("Could not parse user email"); - } - public UserRole GetUserRole() { var claim = _httpContextAccessor.HttpContext?.User.Claims @@ -56,4 +43,17 @@ public sealed class ApiUser : IUser } public string Name => _httpContextAccessor.HttpContext?.User.Identity?.Name ?? string.Empty; -} + + public string GetUserEmail() + { + var claim = _httpContextAccessor.HttpContext?.User.Claims + .FirstOrDefault(x => string.Equals(x.Type, ClaimTypes.Email)); + + if (!string.IsNullOrWhiteSpace(claim?.Value)) + { + return claim?.Value!; + } + + throw new ArgumentException("Could not parse user email"); + } +} \ No newline at end of file diff --git a/CleanArchitecture.Domain/CleanArchitecture.Domain.csproj b/CleanArchitecture.Domain/CleanArchitecture.Domain.csproj index bd7f84a..3774cae 100644 --- a/CleanArchitecture.Domain/CleanArchitecture.Domain.csproj +++ b/CleanArchitecture.Domain/CleanArchitecture.Domain.csproj @@ -6,12 +6,12 @@ - - - - - - + + + + + + diff --git a/CleanArchitecture.Domain/Commands/CommandBase.cs b/CleanArchitecture.Domain/Commands/CommandBase.cs index 25f84f6..02d78d1 100644 --- a/CleanArchitecture.Domain/Commands/CommandBase.cs +++ b/CleanArchitecture.Domain/Commands/CommandBase.cs @@ -6,17 +6,17 @@ namespace CleanArchitecture.Domain.Commands; public abstract class CommandBase : IRequest { - public Guid AggregateId { get; } - public string MessageType { get; } - public DateTime Timestamp { get; } - public ValidationResult? ValidationResult { get; protected set; } - protected CommandBase(Guid aggregateId) { MessageType = GetType().Name; Timestamp = DateTime.Now; AggregateId = aggregateId; } - + + public Guid AggregateId { get; } + public string MessageType { get; } + public DateTime Timestamp { get; } + public ValidationResult? ValidationResult { get; protected set; } + public abstract bool IsValid(); } \ No newline at end of file diff --git a/CleanArchitecture.Domain/Commands/CommandHandlerBase.cs b/CleanArchitecture.Domain/Commands/CommandHandlerBase.cs index fe9b36c..14d6877 100644 --- a/CleanArchitecture.Domain/Commands/CommandHandlerBase.cs +++ b/CleanArchitecture.Domain/Commands/CommandHandlerBase.cs @@ -10,8 +10,8 @@ namespace CleanArchitecture.Domain.Commands; public abstract class CommandHandlerBase { protected readonly IMediatorHandler _bus; - private readonly IUnitOfWork _unitOfWork; private readonly DomainNotificationHandler _notifications; + private readonly IUnitOfWork _unitOfWork; protected CommandHandlerBase( IMediatorHandler bus, @@ -22,7 +22,7 @@ public abstract class CommandHandlerBase _unitOfWork = unitOfWork; _notifications = (DomainNotificationHandler)notifications; } - + public async Task CommitAsync() { if (_notifications.HasNotifications()) @@ -43,7 +43,7 @@ public abstract class CommandHandlerBase return false; } - + protected async Task NotifyAsync(string key, string message, string code) { await _bus.RaiseEventAsync( @@ -54,7 +54,7 @@ public abstract class CommandHandlerBase { await _bus.RaiseEventAsync(notification); } - + protected async ValueTask TestValidityAsync(CommandBase command) { if (command.IsValid()) @@ -71,8 +71,8 @@ public abstract class CommandHandlerBase { await NotifyAsync( new DomainNotification( - command.MessageType, - error.ErrorMessage, + command.MessageType, + error.ErrorMessage, error.ErrorCode, error.FormattedMessagePlaceholderValues)); } diff --git a/CleanArchitecture.Domain/Commands/Users/ChangePassword/ChangePasswordCommand.cs b/CleanArchitecture.Domain/Commands/Users/ChangePassword/ChangePasswordCommand.cs index 44f0d86..4a27534 100644 --- a/CleanArchitecture.Domain/Commands/Users/ChangePassword/ChangePasswordCommand.cs +++ b/CleanArchitecture.Domain/Commands/Users/ChangePassword/ChangePasswordCommand.cs @@ -6,15 +6,15 @@ public sealed class ChangePasswordCommand : CommandBase { private readonly ChangePasswordCommandValidation _validation = new(); - public string Password { get; } - public string NewPassword { get; } - public ChangePasswordCommand(string password, string newPassword) : base(Guid.NewGuid()) { Password = password; NewPassword = newPassword; } + public string Password { get; } + public string NewPassword { get; } + public override bool IsValid() { ValidationResult = _validation.Validate(this); diff --git a/CleanArchitecture.Domain/Commands/Users/ChangePassword/ChangePasswordCommandHandler.cs b/CleanArchitecture.Domain/Commands/Users/ChangePassword/ChangePasswordCommandHandler.cs index 4bd41e0..8b5ef24 100644 --- a/CleanArchitecture.Domain/Commands/Users/ChangePassword/ChangePasswordCommandHandler.cs +++ b/CleanArchitecture.Domain/Commands/Users/ChangePassword/ChangePasswordCommandHandler.cs @@ -13,8 +13,8 @@ namespace CleanArchitecture.Domain.Commands.Users.ChangePassword; public sealed class ChangePasswordCommandHandler : CommandHandlerBase, IRequestHandler { - private readonly IUserRepository _userRepository; private readonly IUser _user; + private readonly IUserRepository _userRepository; public ChangePasswordCommandHandler( IMediatorHandler bus, @@ -68,4 +68,4 @@ public sealed class ChangePasswordCommandHandler : CommandHandlerBase, await _bus.RaiseEventAsync(new PasswordChangedEvent(user.Id)); } } -} +} \ No newline at end of file diff --git a/CleanArchitecture.Domain/Commands/Users/ChangePassword/ChangePasswordCommandValidation.cs b/CleanArchitecture.Domain/Commands/Users/ChangePassword/ChangePasswordCommandValidation.cs index e90e964..83ea0d9 100644 --- a/CleanArchitecture.Domain/Commands/Users/ChangePassword/ChangePasswordCommandValidation.cs +++ b/CleanArchitecture.Domain/Commands/Users/ChangePassword/ChangePasswordCommandValidation.cs @@ -22,4 +22,4 @@ public sealed class ChangePasswordCommandValidation : AbstractValidator cmd.NewPassword) .Password(); } -} +} \ No newline at end of file diff --git a/CleanArchitecture.Domain/Commands/Users/CreateUser/CreateUserCommand.cs b/CleanArchitecture.Domain/Commands/Users/CreateUser/CreateUserCommand.cs index 7185a16..bafd286 100644 --- a/CleanArchitecture.Domain/Commands/Users/CreateUser/CreateUserCommand.cs +++ b/CleanArchitecture.Domain/Commands/Users/CreateUser/CreateUserCommand.cs @@ -4,13 +4,7 @@ namespace CleanArchitecture.Domain.Commands.Users.CreateUser; public sealed class CreateUserCommand : CommandBase { - private readonly CreateUserCommandValidation _validation = new(); - - public Guid UserId { get; } - public string Email { get; } - public string Surname { get; } - public string GivenName { get; } - public string Password { get; } + private readonly CreateUserCommandValidation _validation = new(); public CreateUserCommand( Guid userId, @@ -26,6 +20,12 @@ public sealed class CreateUserCommand : CommandBase Password = password; } + public Guid UserId { get; } + public string Email { get; } + public string Surname { get; } + public string GivenName { get; } + public string Password { get; } + public override bool IsValid() { ValidationResult = _validation.Validate(this); diff --git a/CleanArchitecture.Domain/Commands/Users/CreateUser/CreateUserCommandHandler.cs b/CleanArchitecture.Domain/Commands/Users/CreateUser/CreateUserCommandHandler.cs index 2f7f511..382a529 100644 --- a/CleanArchitecture.Domain/Commands/Users/CreateUser/CreateUserCommandHandler.cs +++ b/CleanArchitecture.Domain/Commands/Users/CreateUser/CreateUserCommandHandler.cs @@ -16,7 +16,7 @@ public sealed class CreateUserCommandHandler : CommandHandlerBase, IRequestHandler { private readonly IUserRepository _userRepository; - + public CreateUserCommandHandler( IMediatorHandler bus, IUnitOfWork unitOfWork, @@ -44,9 +44,9 @@ public sealed class CreateUserCommandHandler : CommandHandlerBase, DomainErrorCodes.UserAlreadyExists)); return; } - + existingUser = await _userRepository.GetByEmailAsync(request.Email); - + if (existingUser != null) { await _bus.RaiseEventAsync( @@ -60,15 +60,15 @@ public sealed class CreateUserCommandHandler : CommandHandlerBase, var passwordHash = BC.HashPassword(request.Password); var user = new User( - request.UserId, + request.UserId, request.Email, request.Surname, request.GivenName, passwordHash, UserRole.User); - + _userRepository.Add(user); - + if (await CommitAsync()) { await _bus.RaiseEventAsync(new UserCreatedEvent(user.Id)); diff --git a/CleanArchitecture.Domain/Commands/Users/CreateUser/CreateUserCommandValidation.cs b/CleanArchitecture.Domain/Commands/Users/CreateUser/CreateUserCommandValidation.cs index e1b7ed9..23be1aa 100644 --- a/CleanArchitecture.Domain/Commands/Users/CreateUser/CreateUserCommandValidation.cs +++ b/CleanArchitecture.Domain/Commands/Users/CreateUser/CreateUserCommandValidation.cs @@ -22,7 +22,7 @@ public sealed class CreateUserCommandValidation : AbstractValidator cmd.Email) @@ -33,7 +33,7 @@ public sealed class CreateUserCommandValidation : AbstractValidator cmd.Surname) @@ -44,7 +44,7 @@ public sealed class CreateUserCommandValidation : AbstractValidator cmd.GivenName) @@ -55,7 +55,7 @@ public sealed class CreateUserCommandValidation : AbstractValidator cmd.Password) diff --git a/CleanArchitecture.Domain/Commands/Users/DeleteUser/DeleteUserCommand.cs b/CleanArchitecture.Domain/Commands/Users/DeleteUser/DeleteUserCommand.cs index a15d7d6..48c42f5 100644 --- a/CleanArchitecture.Domain/Commands/Users/DeleteUser/DeleteUserCommand.cs +++ b/CleanArchitecture.Domain/Commands/Users/DeleteUser/DeleteUserCommand.cs @@ -4,15 +4,15 @@ namespace CleanArchitecture.Domain.Commands.Users.DeleteUser; public sealed class DeleteUserCommand : CommandBase { - private readonly DeleteUserCommandValidation _validation = new(); - - public Guid UserId { get; } - + private readonly DeleteUserCommandValidation _validation = new(); + public DeleteUserCommand(Guid userId) : base(userId) { UserId = userId; } + public Guid UserId { get; } + public override bool IsValid() { ValidationResult = _validation.Validate(this); diff --git a/CleanArchitecture.Domain/Commands/Users/DeleteUser/DeleteUserCommandHandler.cs b/CleanArchitecture.Domain/Commands/Users/DeleteUser/DeleteUserCommandHandler.cs index 366c240..d40495f 100644 --- a/CleanArchitecture.Domain/Commands/Users/DeleteUser/DeleteUserCommandHandler.cs +++ b/CleanArchitecture.Domain/Commands/Users/DeleteUser/DeleteUserCommandHandler.cs @@ -13,8 +13,8 @@ namespace CleanArchitecture.Domain.Commands.Users.DeleteUser; public sealed class DeleteUserCommandHandler : CommandHandlerBase, IRequestHandler { - private readonly IUserRepository _userRepository; private readonly IUser _user; + private readonly IUserRepository _userRepository; public DeleteUserCommandHandler( IMediatorHandler bus, @@ -35,7 +35,7 @@ public sealed class DeleteUserCommandHandler : CommandHandlerBase, } var user = await _userRepository.GetByIdAsync(request.UserId); - + if (user == null) { await NotifyAsync( @@ -54,7 +54,7 @@ public sealed class DeleteUserCommandHandler : CommandHandlerBase, request.MessageType, $"No permission to delete user {request.UserId}", ErrorCodes.InsufficientPermissions)); - + return; } diff --git a/CleanArchitecture.Domain/Commands/Users/LoginUser/LoginUserCommand.cs b/CleanArchitecture.Domain/Commands/Users/LoginUser/LoginUserCommand.cs index b0a7999..2fcdc00 100644 --- a/CleanArchitecture.Domain/Commands/Users/LoginUser/LoginUserCommand.cs +++ b/CleanArchitecture.Domain/Commands/Users/LoginUser/LoginUserCommand.cs @@ -8,9 +8,6 @@ public sealed class LoginUserCommand : CommandBase, { private readonly LoginUserCommandValidation _validation = new(); - public string Email { get; set; } - public string Password { get; set; } - public LoginUserCommand( string email, @@ -20,6 +17,9 @@ public sealed class LoginUserCommand : CommandBase, Password = password; } + public string Email { get; set; } + public string Password { get; set; } + public override bool IsValid() { ValidationResult = _validation.Validate(this); diff --git a/CleanArchitecture.Domain/Commands/Users/LoginUser/LoginUserCommandHandler.cs b/CleanArchitecture.Domain/Commands/Users/LoginUser/LoginUserCommandHandler.cs index 9998952..c93e2c1 100644 --- a/CleanArchitecture.Domain/Commands/Users/LoginUser/LoginUserCommandHandler.cs +++ b/CleanArchitecture.Domain/Commands/Users/LoginUser/LoginUserCommandHandler.cs @@ -1,6 +1,7 @@ -using System.Security.Claims; +using System; +using System.IdentityModel.Tokens.Jwt; +using System.Security.Claims; using System.Text; -using System; using System.Threading; using System.Threading.Tasks; using CleanArchitecture.Domain.Enums; @@ -10,10 +11,9 @@ using CleanArchitecture.Domain.Interfaces.Repositories; using CleanArchitecture.Domain.Notifications; using CleanArchitecture.Domain.Settings; using MediatR; -using BC = BCrypt.Net.BCrypt; -using System.IdentityModel.Tokens.Jwt; -using Microsoft.IdentityModel.Tokens; using Microsoft.Extensions.Options; +using Microsoft.IdentityModel.Tokens; +using BC = BCrypt.Net.BCrypt; namespace CleanArchitecture.Domain.Commands.Users.LoginUser; @@ -21,9 +21,9 @@ public sealed class LoginUserCommandHandler : CommandHandlerBase, IRequestHandler { private const double ExpiryDurationMinutes = 30; + private readonly TokenSettings _tokenSettings; private readonly IUserRepository _userRepository; - private readonly TokenSettings _tokenSettings; public LoginUserCommandHandler( IMediatorHandler bus, @@ -80,10 +80,10 @@ public sealed class LoginUserCommandHandler : CommandHandlerBase, { var claims = new[] { - new Claim(ClaimTypes.Email, email), - new Claim(ClaimTypes.Role, role.ToString()), - new Claim(ClaimTypes.NameIdentifier, id.ToString()) - }; + new Claim(ClaimTypes.Email, email), + new Claim(ClaimTypes.Role, role.ToString()), + new Claim(ClaimTypes.NameIdentifier, id.ToString()) + }; var securityKey = new SymmetricSecurityKey( Encoding.UTF8.GetBytes(tokenSettings.Secret)); @@ -101,4 +101,4 @@ public sealed class LoginUserCommandHandler : CommandHandlerBase, return new JwtSecurityTokenHandler().WriteToken(tokenDescriptor); } -} +} \ No newline at end of file diff --git a/CleanArchitecture.Domain/Commands/Users/LoginUser/LoginUserCommandValidation.cs b/CleanArchitecture.Domain/Commands/Users/LoginUser/LoginUserCommandValidation.cs index 64bc452..9f668cd 100644 --- a/CleanArchitecture.Domain/Commands/Users/LoginUser/LoginUserCommandValidation.cs +++ b/CleanArchitecture.Domain/Commands/Users/LoginUser/LoginUserCommandValidation.cs @@ -28,4 +28,4 @@ public sealed class LoginUserCommandValidation : AbstractValidator cmd.Password) .Password(); } -} +} \ No newline at end of file diff --git a/CleanArchitecture.Domain/Commands/Users/UpdateUser/UpdateUserCommand.cs b/CleanArchitecture.Domain/Commands/Users/UpdateUser/UpdateUserCommand.cs index d561e47..918bec9 100644 --- a/CleanArchitecture.Domain/Commands/Users/UpdateUser/UpdateUserCommand.cs +++ b/CleanArchitecture.Domain/Commands/Users/UpdateUser/UpdateUserCommand.cs @@ -6,12 +6,6 @@ namespace CleanArchitecture.Domain.Commands.Users.UpdateUser; public sealed class UpdateUserCommand : CommandBase { private readonly UpdateUserCommandValidation _validation = new(); - - public Guid UserId { get; } - public string Email { get; } - public string Surname { get; } - public string GivenName { get; } - public UserRole Role { get; } public UpdateUserCommand( Guid userId, @@ -27,6 +21,12 @@ public sealed class UpdateUserCommand : CommandBase Role = role; } + public Guid UserId { get; } + public string Email { get; } + public string Surname { get; } + public string GivenName { get; } + public UserRole Role { get; } + public override bool IsValid() { ValidationResult = _validation.Validate(this); diff --git a/CleanArchitecture.Domain/Commands/Users/UpdateUser/UpdateUserCommandHandler.cs b/CleanArchitecture.Domain/Commands/Users/UpdateUser/UpdateUserCommandHandler.cs index bbf140c..02d87cd 100644 --- a/CleanArchitecture.Domain/Commands/Users/UpdateUser/UpdateUserCommandHandler.cs +++ b/CleanArchitecture.Domain/Commands/Users/UpdateUser/UpdateUserCommandHandler.cs @@ -1,6 +1,5 @@ using System.Threading; using System.Threading.Tasks; -using CleanArchitecture.Domain.Entities; using CleanArchitecture.Domain.Enums; using CleanArchitecture.Domain.Errors; using CleanArchitecture.Domain.Events.User; @@ -14,8 +13,8 @@ namespace CleanArchitecture.Domain.Commands.Users.UpdateUser; public sealed class UpdateUserCommandHandler : CommandHandlerBase, IRequestHandler { - private readonly IUserRepository _userRepository; private readonly IUser _user; + private readonly IUserRepository _userRepository; public UpdateUserCommandHandler( IMediatorHandler bus, @@ -54,7 +53,7 @@ public sealed class UpdateUserCommandHandler : CommandHandlerBase, request.MessageType, $"No permission to update user {request.UserId}", ErrorCodes.InsufficientPermissions)); - + return; } @@ -68,7 +67,7 @@ public sealed class UpdateUserCommandHandler : CommandHandlerBase, user.SetGivenName(request.GivenName); _userRepository.Update(user); - + if (await CommitAsync()) { await _bus.RaiseEventAsync(new UserUpdatedEvent(user.Id)); diff --git a/CleanArchitecture.Domain/Commands/Users/UpdateUser/UpdateUserCommandValidation.cs b/CleanArchitecture.Domain/Commands/Users/UpdateUser/UpdateUserCommandValidation.cs index 58a901f..f0afc67 100644 --- a/CleanArchitecture.Domain/Commands/Users/UpdateUser/UpdateUserCommandValidation.cs +++ b/CleanArchitecture.Domain/Commands/Users/UpdateUser/UpdateUserCommandValidation.cs @@ -21,7 +21,7 @@ public sealed class UpdateUserCommandValidation : AbstractValidator cmd.Email) @@ -32,7 +32,7 @@ public sealed class UpdateUserCommandValidation : AbstractValidator cmd.Surname) @@ -43,7 +43,7 @@ public sealed class UpdateUserCommandValidation : AbstractValidator cmd.GivenName) diff --git a/CleanArchitecture.Domain/Entities/User.cs b/CleanArchitecture.Domain/Entities/User.cs index 2d14858..b92cd3e 100644 --- a/CleanArchitecture.Domain/Entities/User.cs +++ b/CleanArchitecture.Domain/Entities/User.cs @@ -6,14 +6,6 @@ namespace CleanArchitecture.Domain.Entities; public class User : Entity { - public string Email { get; private set; } - public string GivenName { get; private set; } - public string Surname { get; private set; } - public string Password { get; private set; } - public UserRole Role { get; private set; } - - public string FullName => $"{Surname}, {GivenName}"; - public User( Guid id, string email, @@ -29,6 +21,14 @@ public class User : Entity Role = role; } + public string Email { get; private set; } + public string GivenName { get; private set; } + public string Surname { get; private set; } + public string Password { get; private set; } + public UserRole Role { get; private set; } + + public string FullName => $"{Surname}, {GivenName}"; + [MemberNotNull(nameof(Email))] public void SetEmail(string email) { @@ -45,7 +45,7 @@ public class User : Entity Email = email; } - + [MemberNotNull(nameof(GivenName))] public void SetGivenName(string givenName) { @@ -62,7 +62,7 @@ public class User : Entity GivenName = givenName; } - + [MemberNotNull(nameof(Surname))] public void SetSurname(string surname) { diff --git a/CleanArchitecture.Domain/Enums/UserRole.cs b/CleanArchitecture.Domain/Enums/UserRole.cs index 95c9259..f95d8e1 100644 --- a/CleanArchitecture.Domain/Enums/UserRole.cs +++ b/CleanArchitecture.Domain/Enums/UserRole.cs @@ -4,4 +4,4 @@ public enum UserRole { Admin, User -} +} \ No newline at end of file diff --git a/CleanArchitecture.Domain/Errors/DomainErrorCodes.cs b/CleanArchitecture.Domain/Errors/DomainErrorCodes.cs index 76925e2..6c486cf 100644 --- a/CleanArchitecture.Domain/Errors/DomainErrorCodes.cs +++ b/CleanArchitecture.Domain/Errors/DomainErrorCodes.cs @@ -20,7 +20,7 @@ public static class DomainErrorCodes public const string UserLowercaseLetterPassword = "USER_PASSWORD_MUST_CONTAIN_A_LOWERCASE_LETTER"; public const string UserNumberPassword = "USER_PASSWORD_MUST_CONTAIN_A_NUMBER"; public const string UserSpecialCharPassword = "USER_PASSWORD_MUST_CONTAIN_A_SPECIAL_CHARACTER"; - + // User public const string UserAlreadyExists = "USER_ALREADY_EXISTS"; public const string UserPasswordIncorrect = "USER_PASSWORD_INCORRECT"; diff --git a/CleanArchitecture.Domain/EventHandler/UserEventHandler.cs b/CleanArchitecture.Domain/EventHandler/UserEventHandler.cs index a6f7e47..3e29e28 100644 --- a/CleanArchitecture.Domain/EventHandler/UserEventHandler.cs +++ b/CleanArchitecture.Domain/EventHandler/UserEventHandler.cs @@ -11,7 +11,7 @@ public sealed class UserEventHandler : INotificationHandler, INotificationHandler { - public Task Handle(UserDeletedEvent notification, CancellationToken cancellationToken) + public Task Handle(PasswordChangedEvent notification, CancellationToken cancellationToken) { return Task.CompletedTask; } @@ -20,13 +20,13 @@ public sealed class UserEventHandler : { return Task.CompletedTask; } - - public Task Handle(UserUpdatedEvent notification, CancellationToken cancellationToken) + + public Task Handle(UserDeletedEvent notification, CancellationToken cancellationToken) { return Task.CompletedTask; } - public Task Handle(PasswordChangedEvent notification, CancellationToken cancellationToken) + public Task Handle(UserUpdatedEvent notification, CancellationToken cancellationToken) { return Task.CompletedTask; } diff --git a/CleanArchitecture.Domain/Events/User/PasswordChangedEvent.cs b/CleanArchitecture.Domain/Events/User/PasswordChangedEvent.cs index 32e7a53..7ebb021 100644 --- a/CleanArchitecture.Domain/Events/User/PasswordChangedEvent.cs +++ b/CleanArchitecture.Domain/Events/User/PasswordChangedEvent.cs @@ -4,10 +4,10 @@ namespace CleanArchitecture.Domain.Events.User; public sealed class PasswordChangedEvent : DomainEvent { - public Guid UserId { get; } - public PasswordChangedEvent(Guid userId) : base(userId) { UserId = userId; } -} + + public Guid UserId { get; } +} \ No newline at end of file diff --git a/CleanArchitecture.Domain/Events/User/UserCreatedEvent.cs b/CleanArchitecture.Domain/Events/User/UserCreatedEvent.cs index 19f3658..2a67c9b 100644 --- a/CleanArchitecture.Domain/Events/User/UserCreatedEvent.cs +++ b/CleanArchitecture.Domain/Events/User/UserCreatedEvent.cs @@ -4,10 +4,10 @@ namespace CleanArchitecture.Domain.Events.User; public sealed class UserCreatedEvent : DomainEvent { - public Guid UserId { get; } - public UserCreatedEvent(Guid userId) : base(userId) { UserId = userId; } + + public Guid UserId { get; } } \ No newline at end of file diff --git a/CleanArchitecture.Domain/Events/User/UserDeletedEvent.cs b/CleanArchitecture.Domain/Events/User/UserDeletedEvent.cs index 576bcbf..89fadc1 100644 --- a/CleanArchitecture.Domain/Events/User/UserDeletedEvent.cs +++ b/CleanArchitecture.Domain/Events/User/UserDeletedEvent.cs @@ -4,10 +4,10 @@ namespace CleanArchitecture.Domain.Events.User; public sealed class UserDeletedEvent : DomainEvent { - public Guid UserId { get; } - public UserDeletedEvent(Guid userId) : base(userId) { UserId = userId; } + + public Guid UserId { get; } } \ No newline at end of file diff --git a/CleanArchitecture.Domain/Events/User/UserUpdatedEvent.cs b/CleanArchitecture.Domain/Events/User/UserUpdatedEvent.cs index 996c7e6..92a09cf 100644 --- a/CleanArchitecture.Domain/Events/User/UserUpdatedEvent.cs +++ b/CleanArchitecture.Domain/Events/User/UserUpdatedEvent.cs @@ -4,10 +4,10 @@ namespace CleanArchitecture.Domain.Events.User; public sealed class UserUpdatedEvent : DomainEvent { - public Guid UserId { get; } - public UserUpdatedEvent(Guid userId) : base(userId) { UserId = userId; } + + public Guid UserId { get; } } \ No newline at end of file diff --git a/CleanArchitecture.Domain/Extensions/ServiceCollectionExtension.cs b/CleanArchitecture.Domain/Extensions/ServiceCollectionExtension.cs index d56c338..4c3b2b9 100644 --- a/CleanArchitecture.Domain/Extensions/ServiceCollectionExtension.cs +++ b/CleanArchitecture.Domain/Extensions/ServiceCollectionExtension.cs @@ -25,7 +25,7 @@ public static class ServiceCollectionExtension return services; } - + public static IServiceCollection AddNotificationHandlers(this IServiceCollection services) { // User @@ -41,7 +41,7 @@ public static class ServiceCollectionExtension { // User services.AddScoped(); - + return services; } } \ No newline at end of file diff --git a/CleanArchitecture.Domain/Extensions/Validation/CustomValidator.cs b/CleanArchitecture.Domain/Extensions/Validation/CustomValidator.cs index ecacf87..c94ff0d 100644 --- a/CleanArchitecture.Domain/Extensions/Validation/CustomValidator.cs +++ b/CleanArchitecture.Domain/Extensions/Validation/CustomValidator.cs @@ -17,7 +17,10 @@ public static class CustomValidator return base64.Length % 4 == 0 && Regex.IsMatch(base64, @"^[a-zA-Z0-9\+/]*={0,3}$", RegexOptions.None); } - public static IRuleBuilder Password(this IRuleBuilder ruleBuilder, int minLength = 8, int maxLength = 50) + public static IRuleBuilder Password( + this IRuleBuilder ruleBuilder, + int minLength = 8, + int maxLength = 50) { var options = ruleBuilder .NotEmpty().WithErrorCode(DomainErrorCodes.UserEmptyPassword) diff --git a/CleanArchitecture.Domain/Interfaces/IUser.cs b/CleanArchitecture.Domain/Interfaces/IUser.cs index 55e953c..26cbe1a 100644 --- a/CleanArchitecture.Domain/Interfaces/IUser.cs +++ b/CleanArchitecture.Domain/Interfaces/IUser.cs @@ -5,8 +5,7 @@ namespace CleanArchitecture.Domain.Interfaces; public interface IUser { + string Name { get; } Guid GetUserId(); UserRole GetUserRole(); - - string Name { get; } -} +} \ No newline at end of file diff --git a/CleanArchitecture.Domain/Notifications/DomainNotification.cs b/CleanArchitecture.Domain/Notifications/DomainNotification.cs index c9b2719..e57a891 100644 --- a/CleanArchitecture.Domain/Notifications/DomainNotification.cs +++ b/CleanArchitecture.Domain/Notifications/DomainNotification.cs @@ -4,11 +4,6 @@ namespace CleanArchitecture.Domain.Notifications; public sealed class DomainNotification : DomainEvent { - public string Key { get; private set; } - public string Value { get; private set; } - public string Code { get; private set; } - public object? Data { get; set; } - public DomainNotification( string key, string value, @@ -23,4 +18,9 @@ public sealed class DomainNotification : DomainEvent Data = data; } + + public string Key { get; } + public string Value { get; } + public string Code { get; } + public object? Data { get; set; } } \ No newline at end of file diff --git a/CleanArchitecture.Domain/Notifications/DomainNotificationHandler.cs b/CleanArchitecture.Domain/Notifications/DomainNotificationHandler.cs index d1e579d..59f9235 100644 --- a/CleanArchitecture.Domain/Notifications/DomainNotificationHandler.cs +++ b/CleanArchitecture.Domain/Notifications/DomainNotificationHandler.cs @@ -15,18 +15,18 @@ public class DomainNotificationHandler : INotificationHandler(); } - public virtual List GetNotifications() - { - return _notifications; - } - public Task Handle(DomainNotification notification, CancellationToken cancellationToken = default) { _notifications.Add(notification); return Task.CompletedTask; } - + + public virtual List GetNotifications() + { + return _notifications; + } + public virtual bool HasNotifications() { return GetNotifications().Any(); diff --git a/CleanArchitecture.Infrastructure.Tests/CleanArchitecture.Infrastructure.Tests.csproj b/CleanArchitecture.Infrastructure.Tests/CleanArchitecture.Infrastructure.Tests.csproj index 574a54c..45957b8 100644 --- a/CleanArchitecture.Infrastructure.Tests/CleanArchitecture.Infrastructure.Tests.csproj +++ b/CleanArchitecture.Infrastructure.Tests/CleanArchitecture.Infrastructure.Tests.csproj @@ -8,10 +8,10 @@ - - - - + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all @@ -23,7 +23,7 @@ - + diff --git a/CleanArchitecture.Infrastructure.Tests/DomainNotificationHandlerTests.cs b/CleanArchitecture.Infrastructure.Tests/DomainNotificationHandlerTests.cs index 95148af..4ebf2a2 100644 --- a/CleanArchitecture.Infrastructure.Tests/DomainNotificationHandlerTests.cs +++ b/CleanArchitecture.Infrastructure.Tests/DomainNotificationHandlerTests.cs @@ -16,9 +16,9 @@ public sealed class DomainNotificationHandlerTests [Fact] public void Should_Handle_DomainNotification() { - string key = "Key"; - string value = "Value"; - string code = "Code"; + var key = "Key"; + var value = "Value"; + var code = "Code"; var domainNotification = new DomainNotification(key, value, code); var domainNotificationHandler = new DomainNotificationHandler(); @@ -29,9 +29,9 @@ public sealed class DomainNotificationHandlerTests [Fact] public void Should_Handle_DomainNotification_Overload() { - string key = "Key"; - string value = "Value"; - string code = "Code"; + var key = "Key"; + var value = "Value"; + var code = "Code"; var domainNotification = new DomainNotification(key, value, code); var domainNotificationHandler = new DomainNotificationHandler(); @@ -42,9 +42,9 @@ public sealed class DomainNotificationHandlerTests [Fact] public void DomainNotification_HasNotifications_After_Handling_One() { - string key = "Key"; - string value = "Value"; - string code = "Code"; + var key = "Key"; + var value = "Value"; + var code = "Code"; var domainNotification = new DomainNotification(key, value, code); var domainNotificationHandler = new DomainNotificationHandler(); @@ -60,4 +60,4 @@ public sealed class DomainNotificationHandlerTests domainNotificationHandler.HasNotifications().Should().BeFalse(); } -} +} \ No newline at end of file diff --git a/CleanArchitecture.Infrastructure.Tests/DomainNotificationTests.cs b/CleanArchitecture.Infrastructure.Tests/DomainNotificationTests.cs index d942a92..f34d1c0 100644 --- a/CleanArchitecture.Infrastructure.Tests/DomainNotificationTests.cs +++ b/CleanArchitecture.Infrastructure.Tests/DomainNotificationTests.cs @@ -10,9 +10,9 @@ public sealed class DomainNotificationTests [Fact] public void Should_Create_DomainNotification_Instance() { - string key = "Key"; - string value = "Value"; - string code = "Code"; + var key = "Key"; + var value = "Value"; + var code = "Code"; var domainNotification = new DomainNotification( key, value, code); @@ -26,9 +26,9 @@ public sealed class DomainNotificationTests [Fact] public void Should_Create_DomainNotification_Overload_Instance() { - string key = "Key"; - string value = "Value"; - string code = "Code"; + var key = "Key"; + var value = "Value"; + var code = "Code"; var domainNotification = new DomainNotification( key, value, code); @@ -38,4 +38,4 @@ public sealed class DomainNotificationTests domainNotification.Code.Should().Be(code); domainNotification.Should().NotBe(default(Guid)); } -} +} \ No newline at end of file diff --git a/CleanArchitecture.Infrastructure.Tests/UnitOfWorkTests.cs b/CleanArchitecture.Infrastructure.Tests/UnitOfWorkTests.cs index 2a0e51f..5700ae1 100644 --- a/CleanArchitecture.Infrastructure.Tests/UnitOfWorkTests.cs +++ b/CleanArchitecture.Infrastructure.Tests/UnitOfWorkTests.cs @@ -19,18 +19,18 @@ public sealed class UnitOfWorkTests var options = new DbContextOptionsBuilder(); var dbContextMock = new Mock(options.Options); var loggerMock = new Mock>>(); - + dbContextMock .Setup(x => x.SaveChangesAsync(CancellationToken.None)) .Returns(Task.FromResult(1)); - + var unitOfWork = UnitOfWorkTestFixture.GetUnitOfWork(dbContextMock.Object, loggerMock.Object); - + var result = await unitOfWork.CommitAsync(); result.Should().BeTrue(); } - + [Fact] public async Task Should_Commit_Async_Returns_False() { @@ -45,7 +45,7 @@ public sealed class UnitOfWorkTests var unitOfWork = UnitOfWorkTestFixture.GetUnitOfWork(dbContextMock.Object, loggerMock.Object); var result = await unitOfWork.CommitAsync(); - + result.Should().BeFalse(); } diff --git a/CleanArchitecture.Infrastructure/CleanArchitecture.Infrastructure.csproj b/CleanArchitecture.Infrastructure/CleanArchitecture.Infrastructure.csproj index 6b37ff6..01399f8 100644 --- a/CleanArchitecture.Infrastructure/CleanArchitecture.Infrastructure.csproj +++ b/CleanArchitecture.Infrastructure/CleanArchitecture.Infrastructure.csproj @@ -6,18 +6,18 @@ - + - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/CleanArchitecture.Infrastructure/Configurations/UserConfiguration.cs b/CleanArchitecture.Infrastructure/Configurations/UserConfiguration.cs index 6fe7426..4763c9c 100644 --- a/CleanArchitecture.Infrastructure/Configurations/UserConfiguration.cs +++ b/CleanArchitecture.Infrastructure/Configurations/UserConfiguration.cs @@ -24,7 +24,7 @@ public sealed class UserConfiguration : IEntityTypeConfiguration .Property(user => user.Surname) .IsRequired() .HasMaxLength(100); - + builder .Property(user => user.Password) .IsRequired() diff --git a/CleanArchitecture.Infrastructure/Database/ApplicationDbContext.cs b/CleanArchitecture.Infrastructure/Database/ApplicationDbContext.cs index f4b4323..4280c4d 100644 --- a/CleanArchitecture.Infrastructure/Database/ApplicationDbContext.cs +++ b/CleanArchitecture.Infrastructure/Database/ApplicationDbContext.cs @@ -6,12 +6,12 @@ namespace CleanArchitecture.Infrastructure.Database; public class ApplicationDbContext : DbContext { - public DbSet Users { get; set; } = null!; - public ApplicationDbContext(DbContextOptions options) : base(options) { } + public DbSet Users { get; set; } = null!; + protected override void OnModelCreating(ModelBuilder builder) { builder.ApplyConfiguration(new UserConfiguration()); diff --git a/CleanArchitecture.Infrastructure/Extensions/DbContextExtension.cs b/CleanArchitecture.Infrastructure/Extensions/DbContextExtension.cs index 9192d48..869c1ea 100644 --- a/CleanArchitecture.Infrastructure/Extensions/DbContextExtension.cs +++ b/CleanArchitecture.Infrastructure/Extensions/DbContextExtension.cs @@ -18,4 +18,4 @@ public static class DbContextExtension context.Database.Migrate(); } } -} +} \ No newline at end of file diff --git a/CleanArchitecture.Infrastructure/Extensions/ServiceCollectionExtensions.cs b/CleanArchitecture.Infrastructure/Extensions/ServiceCollectionExtensions.cs index ff72bb8..a7a68a8 100644 --- a/CleanArchitecture.Infrastructure/Extensions/ServiceCollectionExtensions.cs +++ b/CleanArchitecture.Infrastructure/Extensions/ServiceCollectionExtensions.cs @@ -22,4 +22,4 @@ public static class ServiceCollectionExtensions return services; } -} +} \ No newline at end of file diff --git a/CleanArchitecture.Infrastructure/InMemoryBus.cs b/CleanArchitecture.Infrastructure/InMemoryBus.cs index 5896d83..2df646c 100644 --- a/CleanArchitecture.Infrastructure/InMemoryBus.cs +++ b/CleanArchitecture.Infrastructure/InMemoryBus.cs @@ -14,7 +14,7 @@ public sealed class InMemoryBus : IMediatorHandler { _mediator = mediator; } - + public Task QueryAsync(IRequest query) { return _mediator.Send(query); diff --git a/CleanArchitecture.Infrastructure/Repositories/BaseRepository.cs b/CleanArchitecture.Infrastructure/Repositories/BaseRepository.cs index 52b768d..db13d69 100644 --- a/CleanArchitecture.Infrastructure/Repositories/BaseRepository.cs +++ b/CleanArchitecture.Infrastructure/Repositories/BaseRepository.cs @@ -50,19 +50,6 @@ public class BaseRepository : IRepository where TEntity : Enti return await DbSet.FindAsync(id); } - public int SaveChanges() - { - return _dbContext.SaveChanges(); - } - - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - _dbContext.Dispose(); - } - } - public virtual void Update(TEntity entity) { DbSet.Update(entity); @@ -72,7 +59,7 @@ public class BaseRepository : IRepository where TEntity : Enti { return DbSet.AnyAsync(entity => entity.Id == id); } - + public void Remove(TEntity entity, bool hardDelete = false) { if (hardDelete) @@ -86,4 +73,16 @@ public class BaseRepository : IRepository where TEntity : Enti } } + public int SaveChanges() + { + return _dbContext.SaveChanges(); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + _dbContext.Dispose(); + } + } } \ No newline at end of file diff --git a/CleanArchitecture.Infrastructure/UnitOfWork.cs b/CleanArchitecture.Infrastructure/UnitOfWork.cs index 97d88a9..735590e 100644 --- a/CleanArchitecture.Infrastructure/UnitOfWork.cs +++ b/CleanArchitecture.Infrastructure/UnitOfWork.cs @@ -45,4 +45,4 @@ public sealed class UnitOfWork : IUnitOfWork where TContext : DbContex _context.Dispose(); } } -} +} \ No newline at end of file diff --git a/CleanArchitecture.IntegrationTests/CleanArchitecture.IntegrationTests.csproj b/CleanArchitecture.IntegrationTests/CleanArchitecture.IntegrationTests.csproj index 5f128a0..5768a6d 100644 --- a/CleanArchitecture.IntegrationTests/CleanArchitecture.IntegrationTests.csproj +++ b/CleanArchitecture.IntegrationTests/CleanArchitecture.IntegrationTests.csproj @@ -8,14 +8,14 @@ - - - - - - - - + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all @@ -27,8 +27,8 @@ - - + + diff --git a/CleanArchitecture.IntegrationTests/Controller/UserControllerTests.cs b/CleanArchitecture.IntegrationTests/Controller/UserControllerTests.cs index 357dd35..a06ecfa 100644 --- a/CleanArchitecture.IntegrationTests/Controller/UserControllerTests.cs +++ b/CleanArchitecture.IntegrationTests/Controller/UserControllerTests.cs @@ -24,12 +24,13 @@ public sealed class UserControllerTests : IClassFixture _fixture = fixture; } - [Fact, Priority(0)] + [Fact] + [Priority(0)] public async Task Should_Create_User() { var user = new CreateUserViewModel( - _fixture.CreatedUserEmail, - "Test", + _fixture.CreatedUserEmail, + "Test", "Email", _fixture.CreatedUserPassword); @@ -43,12 +44,13 @@ public sealed class UserControllerTests : IClassFixture _fixture.CreatedUserId = message!.Data; } - - [Fact, Priority(5)] + + [Fact] + [Priority(5)] public async Task Should_Login_User() { var user = new LoginUserViewModel( - _fixture.CreatedUserEmail, + _fixture.CreatedUserEmail, _fixture.CreatedUserPassword); var response = await _fixture.ServerClient.PostAsJsonAsync("/api/v1/user/login", user); @@ -63,7 +65,8 @@ public sealed class UserControllerTests : IClassFixture _fixture.EnableAuthentication(); } - [Fact, Priority(10)] + [Fact] + [Priority(10)] public async Task Should_Get_Created_Users() { var response = await _fixture.ServerClient.GetAsync("/api/v1/user/" + _fixture.CreatedUserId); @@ -81,8 +84,9 @@ public sealed class UserControllerTests : IClassFixture content.Surname.Should().Be("Test"); content.GivenName.Should().Be("Email"); } - - [Fact, Priority(10)] + + [Fact] + [Priority(10)] public async Task Should_Get_The_Current_Active_Users() { var response = await _fixture.ServerClient.GetAsync("/api/v1/user/me"); @@ -101,7 +105,8 @@ public sealed class UserControllerTests : IClassFixture content.GivenName.Should().Be("Email"); } - [Fact, Priority(15)] + [Fact] + [Priority(15)] public async Task Should_Update_User() { var user = new UpdateUserViewModel( @@ -124,7 +129,8 @@ public sealed class UserControllerTests : IClassFixture content.Should().BeEquivalentTo(user); } - [Fact, Priority(20)] + [Fact] + [Priority(20)] public async Task Should_Get_Updated_Users() { var response = await _fixture.ServerClient.GetAsync("/api/v1/user/" + _fixture.CreatedUserId); @@ -141,11 +147,12 @@ public sealed class UserControllerTests : IClassFixture content.Email.Should().Be("newtest@email.com"); content.Surname.Should().Be("NewTest"); content.GivenName.Should().Be("NewEmail"); - + _fixture.CreatedUserEmail = content.Email; } - - [Fact, Priority(25)] + + [Fact] + [Priority(25)] public async Task Should_Change_User_Password() { var user = new ChangePasswordViewModel( @@ -163,10 +170,10 @@ public sealed class UserControllerTests : IClassFixture var content = message!.Data; content.Should().BeEquivalentTo(user); - + // Verify the user can login with the new password var login = new LoginUserViewModel( - _fixture.CreatedUserEmail, + _fixture.CreatedUserEmail, _fixture.CreatedUserPassword + "1"); var loginResponse = await _fixture.ServerClient.PostAsJsonAsync("/api/v1/user/login", login); @@ -178,7 +185,8 @@ public sealed class UserControllerTests : IClassFixture loginMessage?.Data.Should().NotBeEmpty(); } - [Fact, Priority(30)] + [Fact] + [Priority(30)] public async Task Should_Get_All_User() { var response = await _fixture.ServerClient.GetAsync("/api/v1/user"); @@ -192,23 +200,24 @@ public sealed class UserControllerTests : IClassFixture var content = message!.Data!.ToList(); content.Count.Should().Be(2); - + var currentUser = content.First(x => x.Id == _fixture.CreatedUserId); - + currentUser.Id.Should().Be(_fixture.CreatedUserId); currentUser.Role.Should().Be(UserRole.User); currentUser.Email.Should().Be("newtest@email.com"); currentUser.Surname.Should().Be("NewTest"); currentUser.GivenName.Should().Be("NewEmail"); - + var adminUser = content.First(x => x.Role == UserRole.Admin); - + adminUser.Email.Should().Be("admin@email.com"); adminUser.Surname.Should().Be("Admin"); adminUser.GivenName.Should().Be("User"); } - [Fact, Priority(35)] + [Fact] + [Priority(35)] public async Task Should_Delete_User() { var response = await _fixture.ServerClient.DeleteAsync("/api/v1/user/" + _fixture.CreatedUserId); @@ -222,4 +231,4 @@ public sealed class UserControllerTests : IClassFixture var content = message!.Data; content.Should().Be(_fixture.CreatedUserId); } -} +} \ No newline at end of file diff --git a/CleanArchitecture.IntegrationTests/Extensions/FunctionalTestsServiceCollectionExtensions.cs b/CleanArchitecture.IntegrationTests/Extensions/FunctionalTestsServiceCollectionExtensions.cs index 8cb7fad..d81e2c0 100644 --- a/CleanArchitecture.IntegrationTests/Extensions/FunctionalTestsServiceCollectionExtensions.cs +++ b/CleanArchitecture.IntegrationTests/Extensions/FunctionalTestsServiceCollectionExtensions.cs @@ -2,28 +2,29 @@ using System.Collections.Generic; using System.Data.Common; using System.Linq; -using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; using Microsoft.EntityFrameworkCore.Diagnostics; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.Extensions.DependencyInjection; namespace CleanArchitecture.IntegrationTests.Extensions; public static class FunctionalTestsServiceCollectionExtensions { - public static IServiceCollection SetupTestDatabase(this IServiceCollection services, DbConnection connection) where TContext : DbContext + public static IServiceCollection SetupTestDatabase(this IServiceCollection services, + DbConnection connection) where TContext : DbContext { var descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(DbContextOptions)); if (descriptor != null) services.Remove(descriptor); services.AddScoped(p => - DbContextOptionsFactory( - p, - (_, options) => options - .ConfigureWarnings(b => b.Log(CoreEventId.ManyServiceProvidersCreatedWarning)) - .UseLazyLoadingProxies() - .UseSqlite(connection))); + DbContextOptionsFactory( + p, + (_, options) => options + .ConfigureWarnings(b => b.Log(CoreEventId.ManyServiceProvidersCreatedWarning)) + .UseLazyLoadingProxies() + .UseSqlite(connection))); return services; } @@ -42,4 +43,4 @@ public static class FunctionalTestsServiceCollectionExtensions return builder.Options; } -} +} \ No newline at end of file diff --git a/CleanArchitecture.IntegrationTests/Extensions/HttpExtensions.cs b/CleanArchitecture.IntegrationTests/Extensions/HttpExtensions.cs index f765a9c..3ac2378 100644 --- a/CleanArchitecture.IntegrationTests/Extensions/HttpExtensions.cs +++ b/CleanArchitecture.IntegrationTests/Extensions/HttpExtensions.cs @@ -10,7 +10,7 @@ public static class HttpExtensions { private static readonly JsonSerializerOptions JsonSerializerOptions = new() { - PropertyNameCaseInsensitive = true, + PropertyNameCaseInsensitive = true }; private static T? Deserialize(string json) @@ -55,4 +55,4 @@ public static class HttpExtensions return httpClient.PutAsync(url, content); } -} +} \ No newline at end of file diff --git a/CleanArchitecture.IntegrationTests/Fixtures/TestFixtureBase.cs b/CleanArchitecture.IntegrationTests/Fixtures/TestFixtureBase.cs index 2d4994e..cfe5a52 100644 --- a/CleanArchitecture.IntegrationTests/Fixtures/TestFixtureBase.cs +++ b/CleanArchitecture.IntegrationTests/Fixtures/TestFixtureBase.cs @@ -9,8 +9,6 @@ namespace CleanArchitecture.IntegrationTests.Fixtures; public class TestFixtureBase { - public HttpClient ServerClient { get; } - public TestFixtureBase() { var projectDir = Directory.GetCurrentDirectory(); @@ -25,6 +23,8 @@ public class TestFixtureBase ServerClient.Timeout = TimeSpan.FromMinutes(5); } + public HttpClient ServerClient { get; } + protected virtual void SeedTestData(ApplicationDbContext context) { } @@ -35,4 +35,4 @@ public class TestFixtureBase IServiceProvider scopedServices) { } -} +} \ No newline at end of file diff --git a/CleanArchitecture.IntegrationTests/Fixtures/UserTestFixture.cs b/CleanArchitecture.IntegrationTests/Fixtures/UserTestFixture.cs index 3d8fa2e..5769927 100644 --- a/CleanArchitecture.IntegrationTests/Fixtures/UserTestFixture.cs +++ b/CleanArchitecture.IntegrationTests/Fixtures/UserTestFixture.cs @@ -8,9 +8,9 @@ public sealed class UserTestFixture : TestFixtureBase public string CreatedUserEmail { get; set; } = "test@email.com"; public string CreatedUserPassword { get; set; } = "z8]tnayvd5FNLU9:]AQm"; public string CreatedUserToken { get; set; } = string.Empty; - + public void EnableAuthentication() { ServerClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {CreatedUserToken}"); } -} +} \ No newline at end of file diff --git a/CleanArchitecture.IntegrationTests/Infrastructure/CleanArchitectureWebApplicationFactory.cs b/CleanArchitecture.IntegrationTests/Infrastructure/CleanArchitectureWebApplicationFactory.cs index 0e9f243..b35bbdf 100644 --- a/CleanArchitecture.IntegrationTests/Infrastructure/CleanArchitectureWebApplicationFactory.cs +++ b/CleanArchitecture.IntegrationTests/Infrastructure/CleanArchitectureWebApplicationFactory.cs @@ -18,11 +18,11 @@ public sealed class CleanArchitectureWebApplicationFactory : WebApplicationFacto ServiceProvider serviceProvider, IServiceProvider scopedServices); - private readonly SqliteConnection _connection = new($"DataSource=:memory:"); - private readonly AddCustomSeedDataHandler? _addCustomSeedDataHandler; - private readonly RegisterCustomServicesHandler? _registerCustomServicesHandler; + + private readonly SqliteConnection _connection = new("DataSource=:memory:"); private readonly string? _environment; + private readonly RegisterCustomServicesHandler? _registerCustomServicesHandler; public CleanArchitectureWebApplicationFactory( AddCustomSeedDataHandler? addCustomSeedDataHandler, @@ -51,7 +51,7 @@ public sealed class CleanArchitectureWebApplicationFactory : WebApplicationFacto var sp = services.BuildServiceProvider(); - using IServiceScope scope = sp.CreateScope(); + using var scope = sp.CreateScope(); var scopedServices = scope.ServiceProvider; var applicationDbContext = scopedServices.GetRequiredService(); @@ -62,4 +62,4 @@ public sealed class CleanArchitectureWebApplicationFactory : WebApplicationFacto _registerCustomServicesHandler?.Invoke(services, sp, scopedServices); }); } -} +} \ No newline at end of file diff --git a/CleanArchitecture.Proto/CleanArchitecture.Proto.csproj b/CleanArchitecture.Proto/CleanArchitecture.Proto.csproj index 1f914a1..de1ac9c 100644 --- a/CleanArchitecture.Proto/CleanArchitecture.Proto.csproj +++ b/CleanArchitecture.Proto/CleanArchitecture.Proto.csproj @@ -6,19 +6,19 @@ - - + + - - + + - - - + + + diff --git a/CleanArchitecture.gRPC.Tests/CleanArchitecture.gRPC.Tests.csproj b/CleanArchitecture.gRPC.Tests/CleanArchitecture.gRPC.Tests.csproj index 6d11a38..b6443c4 100644 --- a/CleanArchitecture.gRPC.Tests/CleanArchitecture.gRPC.Tests.csproj +++ b/CleanArchitecture.gRPC.Tests/CleanArchitecture.gRPC.Tests.csproj @@ -8,11 +8,11 @@ - - - - - + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all @@ -24,8 +24,8 @@ - - + + diff --git a/CleanArchitecture.gRPC.Tests/Fixtures/UserTestsFixture.cs b/CleanArchitecture.gRPC.Tests/Fixtures/UserTestsFixture.cs index 9cac355..5b04bef 100644 --- a/CleanArchitecture.gRPC.Tests/Fixtures/UserTestsFixture.cs +++ b/CleanArchitecture.gRPC.Tests/Fixtures/UserTestsFixture.cs @@ -11,37 +11,31 @@ namespace CleanArchitecture.gRPC.Tests.Fixtures; public sealed class UserTestsFixture { - private Mock UserRepository { get; } = new (); - - public UsersApiImplementation UsersApiImplementation { get; } - - public IEnumerable ExistingUsers { get; } - public UserTestsFixture() { - ExistingUsers = new List() + ExistingUsers = new List { - new ( - Guid.NewGuid(), - "test@test.de", - "Test First Name", + new( + Guid.NewGuid(), + "test@test.de", + "Test First Name", "Test Last Name", "Test Password", UserRole.User), - new ( - Guid.NewGuid(), - "email@Email.de", - "Email First Name", + new( + Guid.NewGuid(), + "email@Email.de", + "Email First Name", "Email Last Name", "Email Password", UserRole.Admin), - new ( - Guid.NewGuid(), - "user@user.de", - "User First Name", + new( + Guid.NewGuid(), + "user@user.de", + "User First Name", "User Last Name", "User Password", - UserRole.User), + UserRole.User) }; var queryable = ExistingUsers.AsQueryable().BuildMock(); @@ -52,4 +46,10 @@ public sealed class UserTestsFixture UsersApiImplementation = new UsersApiImplementation(UserRepository.Object); } + + private Mock UserRepository { get; } = new(); + + public UsersApiImplementation UsersApiImplementation { get; } + + public IEnumerable ExistingUsers { get; } } \ No newline at end of file diff --git a/CleanArchitecture.gRPC/CleanArchitecture.gRPC.csproj b/CleanArchitecture.gRPC/CleanArchitecture.gRPC.csproj index 82b6f59..1268e7f 100644 --- a/CleanArchitecture.gRPC/CleanArchitecture.gRPC.csproj +++ b/CleanArchitecture.gRPC/CleanArchitecture.gRPC.csproj @@ -6,12 +6,12 @@ - - + + - +