mirror of
https://github.com/alex289/CleanArchitecture.git
synced 2025-06-30 02:31:08 +00:00
chore: Code Cleanup
This commit is contained in:
parent
a3152580a2
commit
61bcab6d77
@ -7,26 +7,26 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AspNetCore.HealthChecks.ApplicationStatus" Version="7.0.0" />
|
<PackageReference Include="AspNetCore.HealthChecks.ApplicationStatus" Version="7.0.0"/>
|
||||||
<PackageReference Include="AspNetCore.HealthChecks.SqlServer" Version="7.0.0" />
|
<PackageReference Include="AspNetCore.HealthChecks.SqlServer" Version="7.0.0"/>
|
||||||
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="7.1.0" />
|
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="7.1.0"/>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.10" />
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.10"/>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.10" />
|
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.10"/>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.10">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.10">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="7.0.10" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="7.0.10"/>
|
||||||
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="7.0.10" />
|
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="7.0.10"/>
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0"/>
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.5.0" />
|
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.5.0"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\CleanArchitecture.Application\CleanArchitecture.Application.csproj" />
|
<ProjectReference Include="..\CleanArchitecture.Application\CleanArchitecture.Application.csproj"/>
|
||||||
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj" />
|
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj"/>
|
||||||
<ProjectReference Include="..\CleanArchitecture.gRPC\CleanArchitecture.gRPC.csproj" />
|
<ProjectReference Include="..\CleanArchitecture.gRPC\CleanArchitecture.gRPC.csproj"/>
|
||||||
<ProjectReference Include="..\CleanArchitecture.Infrastructure\CleanArchitecture.Infrastructure.csproj" />
|
<ProjectReference Include="..\CleanArchitecture.Infrastructure\CleanArchitecture.Infrastructure.csproj"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -18,14 +18,14 @@ namespace CleanArchitecture.Api.Controllers;
|
|||||||
public sealed class TenantController : ApiController
|
public sealed class TenantController : ApiController
|
||||||
{
|
{
|
||||||
private readonly ITenantService _tenantService;
|
private readonly ITenantService _tenantService;
|
||||||
|
|
||||||
public TenantController(
|
public TenantController(
|
||||||
INotificationHandler<DomainNotification> notifications,
|
INotificationHandler<DomainNotification> notifications,
|
||||||
ITenantService tenantService) : base(notifications)
|
ITenantService tenantService) : base(notifications)
|
||||||
{
|
{
|
||||||
_tenantService = tenantService;
|
_tenantService = tenantService;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[SwaggerOperation("Get a list of all tenants")]
|
[SwaggerOperation("Get a list of all tenants")]
|
||||||
[SwaggerResponse(200, "Request successful", typeof(ResponseMessage<IEnumerable<TenantViewModel>>))]
|
[SwaggerResponse(200, "Request successful", typeof(ResponseMessage<IEnumerable<TenantViewModel>>))]
|
||||||
@ -34,7 +34,7 @@ public sealed class TenantController : ApiController
|
|||||||
var tenants = await _tenantService.GetAllTenantsAsync();
|
var tenants = await _tenantService.GetAllTenantsAsync();
|
||||||
return Response(tenants);
|
return Response(tenants);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("{id:guid}")]
|
[HttpGet("{id:guid}")]
|
||||||
[SwaggerOperation("Get a tenant by id")]
|
[SwaggerOperation("Get a tenant by id")]
|
||||||
[SwaggerResponse(200, "Request successful", typeof(ResponseMessage<TenantViewModel>))]
|
[SwaggerResponse(200, "Request successful", typeof(ResponseMessage<TenantViewModel>))]
|
||||||
@ -45,7 +45,7 @@ public sealed class TenantController : ApiController
|
|||||||
var tenant = await _tenantService.GetTenantByIdAsync(id, isDeleted);
|
var tenant = await _tenantService.GetTenantByIdAsync(id, isDeleted);
|
||||||
return Response(tenant);
|
return Response(tenant);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[SwaggerOperation("Create a new tenant")]
|
[SwaggerOperation("Create a new tenant")]
|
||||||
[SwaggerResponse(200, "Request successful", typeof(ResponseMessage<Guid>))]
|
[SwaggerResponse(200, "Request successful", typeof(ResponseMessage<Guid>))]
|
||||||
@ -54,7 +54,7 @@ public sealed class TenantController : ApiController
|
|||||||
var tenantId = await _tenantService.CreateTenantAsync(tenant);
|
var tenantId = await _tenantService.CreateTenantAsync(tenant);
|
||||||
return Response(tenantId);
|
return Response(tenantId);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPut]
|
[HttpPut]
|
||||||
[SwaggerOperation("Update an existing tenant")]
|
[SwaggerOperation("Update an existing tenant")]
|
||||||
[SwaggerResponse(200, "Request successful", typeof(ResponseMessage<UpdateTenantViewModel>))]
|
[SwaggerResponse(200, "Request successful", typeof(ResponseMessage<UpdateTenantViewModel>))]
|
||||||
@ -63,7 +63,7 @@ public sealed class TenantController : ApiController
|
|||||||
await _tenantService.UpdateTenantAsync(tenant);
|
await _tenantService.UpdateTenantAsync(tenant);
|
||||||
return Response(tenant);
|
return Response(tenant);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpDelete("{id:guid}")]
|
[HttpDelete("{id:guid}")]
|
||||||
[SwaggerOperation("Delete an existing tenant")]
|
[SwaggerOperation("Delete an existing tenant")]
|
||||||
[SwaggerResponse(200, "Request successful", typeof(ResponseMessage<Guid>))]
|
[SwaggerResponse(200, "Request successful", typeof(ResponseMessage<Guid>))]
|
||||||
|
@ -60,9 +60,12 @@ public static class ServiceCollectionExtension
|
|||||||
services.AddHttpContextAccessor();
|
services.AddHttpContextAccessor();
|
||||||
|
|
||||||
services.AddAuthentication(
|
services.AddAuthentication(
|
||||||
options => { options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; })
|
options => { options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; })
|
||||||
.AddJwtBearer(
|
.AddJwtBearer(
|
||||||
jwtOptions => { jwtOptions.TokenValidationParameters = CreateTokenValidationParameters(configuration); });
|
jwtOptions =>
|
||||||
|
{
|
||||||
|
jwtOptions.TokenValidationParameters = CreateTokenValidationParameters(configuration);
|
||||||
|
});
|
||||||
|
|
||||||
services
|
services
|
||||||
.AddOptions<TokenSettings>()
|
.AddOptions<TokenSettings>()
|
||||||
@ -90,4 +93,4 @@ public static class ServiceCollectionExtension
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,9 +4,7 @@ namespace CleanArchitecture.Api.Models;
|
|||||||
|
|
||||||
public sealed class DetailedError
|
public sealed class DetailedError
|
||||||
{
|
{
|
||||||
[JsonPropertyName("code")]
|
[JsonPropertyName("code")] public string Code { get; init; } = string.Empty;
|
||||||
public string Code { get; init; } = string.Empty;
|
|
||||||
|
|
||||||
[JsonPropertyName("data")]
|
[JsonPropertyName("data")] public object? Data { get; init; }
|
||||||
public object? Data { get; init; }
|
|
||||||
}
|
}
|
@ -6,15 +6,12 @@ namespace CleanArchitecture.Api.Models;
|
|||||||
|
|
||||||
public sealed class ResponseMessage<T>
|
public sealed class ResponseMessage<T>
|
||||||
{
|
{
|
||||||
[JsonPropertyName("success")]
|
[JsonPropertyName("success")] public bool Success { get; init; }
|
||||||
public bool Success { get; init; }
|
|
||||||
|
|
||||||
[JsonPropertyName("errors")]
|
[JsonPropertyName("errors")] public IEnumerable<string>? Errors { get; init; }
|
||||||
public IEnumerable<string>? Errors { get; init; }
|
|
||||||
|
|
||||||
[JsonPropertyName("detailedErrors")]
|
[JsonPropertyName("detailedErrors")]
|
||||||
public IEnumerable<DetailedError> DetailedErrors { get; init; } = Enumerable.Empty<DetailedError>();
|
public IEnumerable<DetailedError> DetailedErrors { get; init; } = Enumerable.Empty<DetailedError>();
|
||||||
|
|
||||||
[JsonPropertyName("data")]
|
[JsonPropertyName("data")] public T? Data { get; init; }
|
||||||
public T? Data { get; init; }
|
|
||||||
}
|
}
|
@ -61,9 +61,9 @@ var app = builder.Build();
|
|||||||
using (var scope = app.Services.CreateScope())
|
using (var scope = app.Services.CreateScope())
|
||||||
{
|
{
|
||||||
var services = scope.ServiceProvider;
|
var services = scope.ServiceProvider;
|
||||||
ApplicationDbContext appDbContext = services.GetRequiredService<ApplicationDbContext>();
|
var appDbContext = services.GetRequiredService<ApplicationDbContext>();
|
||||||
EventStoreDbContext storeDbContext = services.GetRequiredService<EventStoreDbContext>();
|
var storeDbContext = services.GetRequiredService<EventStoreDbContext>();
|
||||||
DomainNotificationStoreDbContext domainStoreDbContext = services.GetRequiredService<DomainNotificationStoreDbContext>();
|
var domainStoreDbContext = services.GetRequiredService<DomainNotificationStoreDbContext>();
|
||||||
|
|
||||||
appDbContext.EnsureMigrationsApplied();
|
appDbContext.EnsureMigrationsApplied();
|
||||||
|
|
||||||
|
@ -8,11 +8,11 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="FluentAssertions" Version="6.11.0" />
|
<PackageReference Include="FluentAssertions" Version="6.11.0"/>
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.0"/>
|
||||||
<PackageReference Include="MockQueryable.NSubstitute" Version="7.0.0" />
|
<PackageReference Include="MockQueryable.NSubstitute" Version="7.0.0"/>
|
||||||
<PackageReference Include="NSubstitute" Version="5.0.0" />
|
<PackageReference Include="NSubstitute" Version="5.0.0"/>
|
||||||
<PackageReference Include="xunit" Version="2.5.0" />
|
<PackageReference Include="xunit" Version="2.5.0"/>
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
@ -24,8 +24,8 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\CleanArchitecture.Application\CleanArchitecture.Application.csproj" />
|
<ProjectReference Include="..\CleanArchitecture.Application\CleanArchitecture.Application.csproj"/>
|
||||||
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj" />
|
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -11,9 +11,9 @@ public class QueryHandlerBaseFixture
|
|||||||
public QueryHandlerBaseFixture VerifyExistingNotification(string key, string errorCode, string message)
|
public QueryHandlerBaseFixture VerifyExistingNotification(string key, string errorCode, string message)
|
||||||
{
|
{
|
||||||
Bus.Received(1).RaiseEventAsync(Arg.Is<DomainNotification>(notification =>
|
Bus.Received(1).RaiseEventAsync(Arg.Is<DomainNotification>(notification =>
|
||||||
notification.Key == key &&
|
notification.Key == key &&
|
||||||
notification.Code == errorCode &&
|
notification.Code == errorCode &&
|
||||||
notification.Value == message));
|
notification.Value == message));
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -10,16 +10,16 @@ namespace CleanArchitecture.Application.Tests.Fixtures.Queries.Tenants;
|
|||||||
|
|
||||||
public sealed class GetAllTenantsTestFixture : QueryHandlerBaseFixture
|
public sealed class GetAllTenantsTestFixture : QueryHandlerBaseFixture
|
||||||
{
|
{
|
||||||
public GetAllTenantsQueryHandler QueryHandler { get; }
|
|
||||||
private ITenantRepository TenantRepository { get; }
|
|
||||||
|
|
||||||
public GetAllTenantsTestFixture()
|
public GetAllTenantsTestFixture()
|
||||||
{
|
{
|
||||||
TenantRepository = Substitute.For<ITenantRepository>();
|
TenantRepository = Substitute.For<ITenantRepository>();
|
||||||
|
|
||||||
QueryHandler = new(TenantRepository);
|
QueryHandler = new GetAllTenantsQueryHandler(TenantRepository);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public GetAllTenantsQueryHandler QueryHandler { get; }
|
||||||
|
private ITenantRepository TenantRepository { get; }
|
||||||
|
|
||||||
public Tenant SetupTenant(bool deleted = false)
|
public Tenant SetupTenant(bool deleted = false)
|
||||||
{
|
{
|
||||||
var tenant = new Tenant(Guid.NewGuid(), "Tenant 1");
|
var tenant = new Tenant(Guid.NewGuid(), "Tenant 1");
|
||||||
@ -31,7 +31,7 @@ public sealed class GetAllTenantsTestFixture : QueryHandlerBaseFixture
|
|||||||
|
|
||||||
var tenantList = new List<Tenant> { tenant }.BuildMock();
|
var tenantList = new List<Tenant> { tenant }.BuildMock();
|
||||||
TenantRepository.GetAllNoTracking().Returns(tenantList);
|
TenantRepository.GetAllNoTracking().Returns(tenantList);
|
||||||
|
|
||||||
return tenant;
|
return tenant;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -10,18 +10,18 @@ namespace CleanArchitecture.Application.Tests.Fixtures.Queries.Tenants;
|
|||||||
|
|
||||||
public sealed class GetTenantByIdTestFixture : QueryHandlerBaseFixture
|
public sealed class GetTenantByIdTestFixture : QueryHandlerBaseFixture
|
||||||
{
|
{
|
||||||
public GetTenantByIdQueryHandler QueryHandler { get; }
|
|
||||||
private ITenantRepository TenantRepository { get; }
|
|
||||||
|
|
||||||
public GetTenantByIdTestFixture()
|
public GetTenantByIdTestFixture()
|
||||||
{
|
{
|
||||||
TenantRepository = Substitute.For<ITenantRepository>();
|
TenantRepository = Substitute.For<ITenantRepository>();
|
||||||
|
|
||||||
QueryHandler = new(
|
QueryHandler = new GetTenantByIdQueryHandler(
|
||||||
TenantRepository,
|
TenantRepository,
|
||||||
Bus);
|
Bus);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public GetTenantByIdQueryHandler QueryHandler { get; }
|
||||||
|
private ITenantRepository TenantRepository { get; }
|
||||||
|
|
||||||
public Tenant SetupTenant(bool deleted = false)
|
public Tenant SetupTenant(bool deleted = false)
|
||||||
{
|
{
|
||||||
var tenant = new Tenant(Guid.NewGuid(), "Tenant 1");
|
var tenant = new Tenant(Guid.NewGuid(), "Tenant 1");
|
||||||
@ -33,7 +33,7 @@ public sealed class GetTenantByIdTestFixture : QueryHandlerBaseFixture
|
|||||||
|
|
||||||
var tenantList = new List<Tenant> { tenant }.BuildMock();
|
var tenantList = new List<Tenant> { tenant }.BuildMock();
|
||||||
TenantRepository.GetAllNoTracking().Returns(tenantList);
|
TenantRepository.GetAllNoTracking().Returns(tenantList);
|
||||||
|
|
||||||
return tenant;
|
return tenant;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -24,13 +24,13 @@ public sealed class GetAllUsersTestFixture : QueryHandlerBaseFixture
|
|||||||
public void SetupUserAsync()
|
public void SetupUserAsync()
|
||||||
{
|
{
|
||||||
var user = new User(
|
var user = new User(
|
||||||
ExistingUserId,
|
ExistingUserId,
|
||||||
Guid.NewGuid(),
|
Guid.NewGuid(),
|
||||||
"max@mustermann.com",
|
"max@mustermann.com",
|
||||||
"Max",
|
"Max",
|
||||||
"Mustermann",
|
"Mustermann",
|
||||||
"Password",
|
"Password",
|
||||||
UserRole.User);
|
UserRole.User);
|
||||||
|
|
||||||
var query = new[] { user }.BuildMock();
|
var query = new[] { user }.BuildMock();
|
||||||
|
|
||||||
@ -40,13 +40,13 @@ public sealed class GetAllUsersTestFixture : QueryHandlerBaseFixture
|
|||||||
public void SetupDeletedUserAsync()
|
public void SetupDeletedUserAsync()
|
||||||
{
|
{
|
||||||
var user = new User(
|
var user = new User(
|
||||||
ExistingUserId,
|
ExistingUserId,
|
||||||
Guid.NewGuid(),
|
Guid.NewGuid(),
|
||||||
"max@mustermann.com",
|
"max@mustermann.com",
|
||||||
"Max",
|
"Max",
|
||||||
"Mustermann",
|
"Mustermann",
|
||||||
"Password",
|
"Password",
|
||||||
UserRole.User);
|
UserRole.User);
|
||||||
|
|
||||||
user.Delete();
|
user.Delete();
|
||||||
|
|
||||||
|
@ -25,13 +25,13 @@ public sealed class GetUserByIdTestFixture : QueryHandlerBaseFixture
|
|||||||
public void SetupUserAsync()
|
public void SetupUserAsync()
|
||||||
{
|
{
|
||||||
var user = new User(
|
var user = new User(
|
||||||
ExistingUserId,
|
ExistingUserId,
|
||||||
Guid.NewGuid(),
|
Guid.NewGuid(),
|
||||||
"max@mustermann.com",
|
"max@mustermann.com",
|
||||||
"Max",
|
"Max",
|
||||||
"Mustermann",
|
"Mustermann",
|
||||||
"Password",
|
"Password",
|
||||||
UserRole.User);
|
UserRole.User);
|
||||||
|
|
||||||
var query = new[] { user }.BuildMock();
|
var query = new[] { user }.BuildMock();
|
||||||
|
|
||||||
@ -41,13 +41,13 @@ public sealed class GetUserByIdTestFixture : QueryHandlerBaseFixture
|
|||||||
public void SetupDeletedUserAsync()
|
public void SetupDeletedUserAsync()
|
||||||
{
|
{
|
||||||
var user = new User(
|
var user = new User(
|
||||||
ExistingUserId,
|
ExistingUserId,
|
||||||
Guid.NewGuid(),
|
Guid.NewGuid(),
|
||||||
"max@mustermann.com",
|
"max@mustermann.com",
|
||||||
"Max",
|
"Max",
|
||||||
"Mustermann",
|
"Mustermann",
|
||||||
"Password",
|
"Password",
|
||||||
UserRole.User);
|
UserRole.User);
|
||||||
|
|
||||||
user.Delete();
|
user.Delete();
|
||||||
|
|
||||||
|
@ -10,12 +10,12 @@ namespace CleanArchitecture.Application.Tests.Queries.Tenants;
|
|||||||
public sealed class GetAllTenantsQueryHandlerTests
|
public sealed class GetAllTenantsQueryHandlerTests
|
||||||
{
|
{
|
||||||
private readonly GetAllTenantsTestFixture _fixture = new();
|
private readonly GetAllTenantsTestFixture _fixture = new();
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Should_Get_Existing_Tenant()
|
public async Task Should_Get_Existing_Tenant()
|
||||||
{
|
{
|
||||||
var tenant = _fixture.SetupTenant();
|
var tenant = _fixture.SetupTenant();
|
||||||
|
|
||||||
var result = await _fixture.QueryHandler.Handle(
|
var result = await _fixture.QueryHandler.Handle(
|
||||||
new GetAllTenantsQuery(),
|
new GetAllTenantsQuery(),
|
||||||
default);
|
default);
|
||||||
@ -24,12 +24,12 @@ public sealed class GetAllTenantsQueryHandlerTests
|
|||||||
|
|
||||||
tenant.Should().BeEquivalentTo(result.First());
|
tenant.Should().BeEquivalentTo(result.First());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Should_Not_Get_Deleted_Tenant()
|
public async Task Should_Not_Get_Deleted_Tenant()
|
||||||
{
|
{
|
||||||
_fixture.SetupTenant(true);
|
_fixture.SetupTenant(true);
|
||||||
|
|
||||||
var result = await _fixture.QueryHandler.Handle(
|
var result = await _fixture.QueryHandler.Handle(
|
||||||
new GetAllTenantsQuery(),
|
new GetAllTenantsQuery(),
|
||||||
default);
|
default);
|
||||||
|
@ -15,7 +15,7 @@ public sealed class GetTenantByIdQueryHandlerTests
|
|||||||
public async Task Should_Get_Existing_Tenant()
|
public async Task Should_Get_Existing_Tenant()
|
||||||
{
|
{
|
||||||
var tenant = _fixture.SetupTenant();
|
var tenant = _fixture.SetupTenant();
|
||||||
|
|
||||||
var result = await _fixture.QueryHandler.Handle(
|
var result = await _fixture.QueryHandler.Handle(
|
||||||
new GetTenantByIdQuery(tenant.Id, false),
|
new GetTenantByIdQuery(tenant.Id, false),
|
||||||
default);
|
default);
|
||||||
@ -24,12 +24,12 @@ public sealed class GetTenantByIdQueryHandlerTests
|
|||||||
|
|
||||||
tenant.Should().BeEquivalentTo(result);
|
tenant.Should().BeEquivalentTo(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Should_Get_Deleted_Tenant()
|
public async Task Should_Get_Deleted_Tenant()
|
||||||
{
|
{
|
||||||
var tenant = _fixture.SetupTenant(true);
|
var tenant = _fixture.SetupTenant(true);
|
||||||
|
|
||||||
var result = await _fixture.QueryHandler.Handle(
|
var result = await _fixture.QueryHandler.Handle(
|
||||||
new GetTenantByIdQuery(tenant.Id, true),
|
new GetTenantByIdQuery(tenant.Id, true),
|
||||||
default);
|
default);
|
||||||
@ -38,12 +38,12 @@ public sealed class GetTenantByIdQueryHandlerTests
|
|||||||
|
|
||||||
tenant.Should().BeEquivalentTo(result);
|
tenant.Should().BeEquivalentTo(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Should_Not_Get_Deleted_Tenant()
|
public async Task Should_Not_Get_Deleted_Tenant()
|
||||||
{
|
{
|
||||||
var tenant = _fixture.SetupTenant(true);
|
var tenant = _fixture.SetupTenant(true);
|
||||||
|
|
||||||
var result = await _fixture.QueryHandler.Handle(
|
var result = await _fixture.QueryHandler.Handle(
|
||||||
new GetTenantByIdQuery(tenant.Id, false),
|
new GetTenantByIdQuery(tenant.Id, false),
|
||||||
default);
|
default);
|
||||||
|
@ -6,14 +6,13 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.10" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.10"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj" />
|
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj"/>
|
||||||
<ProjectReference Include="..\CleanArchitecture.Proto\CleanArchitecture.Proto.csproj" />
|
<ProjectReference Include="..\CleanArchitecture.Proto\CleanArchitecture.Proto.csproj"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -30,8 +30,9 @@ public static class ServiceCollectionExtension
|
|||||||
|
|
||||||
// Tenant
|
// Tenant
|
||||||
services.AddScoped<IRequestHandler<GetTenantByIdQuery, TenantViewModel?>, GetTenantByIdQueryHandler>();
|
services.AddScoped<IRequestHandler<GetTenantByIdQuery, TenantViewModel?>, GetTenantByIdQueryHandler>();
|
||||||
services.AddScoped<IRequestHandler<GetAllTenantsQuery, IEnumerable<TenantViewModel>>, GetAllTenantsQueryHandler>();
|
services
|
||||||
|
.AddScoped<IRequestHandler<GetAllTenantsQuery, IEnumerable<TenantViewModel>>, GetAllTenantsQueryHandler>();
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,4 +4,4 @@ using MediatR;
|
|||||||
|
|
||||||
namespace CleanArchitecture.Application.Queries.Tenants.GetAll;
|
namespace CleanArchitecture.Application.Queries.Tenants.GetAll;
|
||||||
|
|
||||||
public sealed record GetAllTenantsQuery() : IRequest<IEnumerable<TenantViewModel>>;
|
public sealed record GetAllTenantsQuery : IRequest<IEnumerable<TenantViewModel>>;
|
@ -14,8 +14,8 @@ namespace CleanArchitecture.Application.Queries.Tenants.GetTenantById;
|
|||||||
public sealed class GetTenantByIdQueryHandler :
|
public sealed class GetTenantByIdQueryHandler :
|
||||||
IRequestHandler<GetTenantByIdQuery, TenantViewModel?>
|
IRequestHandler<GetTenantByIdQuery, TenantViewModel?>
|
||||||
{
|
{
|
||||||
private readonly ITenantRepository _tenantRepository;
|
|
||||||
private readonly IMediatorHandler _bus;
|
private readonly IMediatorHandler _bus;
|
||||||
|
private readonly ITenantRepository _tenantRepository;
|
||||||
|
|
||||||
public GetTenantByIdQueryHandler(ITenantRepository tenantRepository, IMediatorHandler bus)
|
public GetTenantByIdQueryHandler(ITenantRepository tenantRepository, IMediatorHandler bus)
|
||||||
{
|
{
|
||||||
|
@ -24,11 +24,11 @@ public sealed class TenantService : ITenantService
|
|||||||
public async Task<Guid> CreateTenantAsync(CreateTenantViewModel tenant)
|
public async Task<Guid> CreateTenantAsync(CreateTenantViewModel tenant)
|
||||||
{
|
{
|
||||||
var tenantId = Guid.NewGuid();
|
var tenantId = Guid.NewGuid();
|
||||||
|
|
||||||
await _bus.SendCommandAsync(new CreateTenantCommand(
|
await _bus.SendCommandAsync(new CreateTenantCommand(
|
||||||
tenantId,
|
tenantId,
|
||||||
tenant.Name));
|
tenant.Name));
|
||||||
|
|
||||||
return tenantId;
|
return tenantId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ public sealed class TenantViewModel
|
|||||||
public Guid Id { get; set; }
|
public Guid Id { get; set; }
|
||||||
public string Name { get; set; } = string.Empty;
|
public string Name { get; set; } = string.Empty;
|
||||||
public IEnumerable<UserViewModel> Users { get; set; } = new List<UserViewModel>();
|
public IEnumerable<UserViewModel> Users { get; set; } = new List<UserViewModel>();
|
||||||
|
|
||||||
public static TenantViewModel FromTenant(Tenant tenant)
|
public static TenantViewModel FromTenant(Tenant tenant)
|
||||||
{
|
{
|
||||||
return new TenantViewModel
|
return new TenantViewModel
|
||||||
|
@ -23,7 +23,7 @@ public sealed class TenantsApiImplementation : TenantsApi.TenantsApiBase
|
|||||||
ServerCallContext context)
|
ServerCallContext context)
|
||||||
{
|
{
|
||||||
var idsAsGuids = new List<Guid>(request.Ids.Count);
|
var idsAsGuids = new List<Guid>(request.Ids.Count);
|
||||||
|
|
||||||
foreach (var id in request.Ids)
|
foreach (var id in request.Ids)
|
||||||
{
|
{
|
||||||
if (Guid.TryParse(id, out var parsed))
|
if (Guid.TryParse(id, out var parsed))
|
||||||
@ -44,7 +44,7 @@ public sealed class TenantsApiImplementation : TenantsApi.TenantsApiBase
|
|||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
var result = new GetTenantsByIdsResult();
|
var result = new GetTenantsByIdsResult();
|
||||||
|
|
||||||
result.Tenants.AddRange(tenants);
|
result.Tenants.AddRange(tenants);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -8,11 +8,11 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
|
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3"/>
|
||||||
<PackageReference Include="FluentAssertions" Version="6.11.0" />
|
<PackageReference Include="FluentAssertions" Version="6.11.0"/>
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.0"/>
|
||||||
<PackageReference Include="NSubstitute" Version="5.0.0" />
|
<PackageReference Include="NSubstitute" Version="5.0.0"/>
|
||||||
<PackageReference Include="xunit" Version="2.5.0" />
|
<PackageReference Include="xunit" Version="2.5.0"/>
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
@ -24,7 +24,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj" />
|
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -26,12 +26,12 @@ public sealed class CreateTenantCommandHandlerTests
|
|||||||
x.AggregateId == command.AggregateId &&
|
x.AggregateId == command.AggregateId &&
|
||||||
x.Name == command.Name);
|
x.Name == command.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Not_Create_Tenant_Insufficient_Permissions()
|
public void Should_Not_Create_Tenant_Insufficient_Permissions()
|
||||||
{
|
{
|
||||||
_fixture.SetupUser();
|
_fixture.SetupUser();
|
||||||
|
|
||||||
var command = new CreateTenantCommand(
|
var command = new CreateTenantCommand(
|
||||||
Guid.NewGuid(),
|
Guid.NewGuid(),
|
||||||
"Test Tenant");
|
"Test Tenant");
|
||||||
@ -46,14 +46,14 @@ public sealed class CreateTenantCommandHandlerTests
|
|||||||
ErrorCodes.InsufficientPermissions,
|
ErrorCodes.InsufficientPermissions,
|
||||||
$"No permission to create tenant {command.AggregateId}");
|
$"No permission to create tenant {command.AggregateId}");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Not_Create_Tenant_Already_Exists()
|
public void Should_Not_Create_Tenant_Already_Exists()
|
||||||
{
|
{
|
||||||
var command = new CreateTenantCommand(
|
var command = new CreateTenantCommand(
|
||||||
Guid.NewGuid(),
|
Guid.NewGuid(),
|
||||||
"Test Tenant");
|
"Test Tenant");
|
||||||
|
|
||||||
_fixture.SetupExistingTenant(command.AggregateId);
|
_fixture.SetupExistingTenant(command.AggregateId);
|
||||||
|
|
||||||
_fixture.CommandHandler.Handle(command, default!).Wait();
|
_fixture.CommandHandler.Handle(command, default!).Wait();
|
||||||
|
@ -8,15 +8,11 @@ namespace CleanArchitecture.Domain.Tests.CommandHandler.Tenant.CreateTenant;
|
|||||||
|
|
||||||
public sealed class CreateTenantCommandTestFixture : CommandHandlerFixtureBase
|
public sealed class CreateTenantCommandTestFixture : CommandHandlerFixtureBase
|
||||||
{
|
{
|
||||||
public CreateTenantCommandHandler CommandHandler { get;}
|
|
||||||
|
|
||||||
private ITenantRepository TenantRepository { get; }
|
|
||||||
|
|
||||||
public CreateTenantCommandTestFixture()
|
public CreateTenantCommandTestFixture()
|
||||||
{
|
{
|
||||||
TenantRepository = Substitute.For<ITenantRepository>();
|
TenantRepository = Substitute.For<ITenantRepository>();
|
||||||
|
|
||||||
CommandHandler = new(
|
CommandHandler = new CreateTenantCommandHandler(
|
||||||
Bus,
|
Bus,
|
||||||
UnitOfWork,
|
UnitOfWork,
|
||||||
NotificationHandler,
|
NotificationHandler,
|
||||||
@ -24,6 +20,10 @@ public sealed class CreateTenantCommandTestFixture : CommandHandlerFixtureBase
|
|||||||
User);
|
User);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CreateTenantCommandHandler CommandHandler { get; }
|
||||||
|
|
||||||
|
private ITenantRepository TenantRepository { get; }
|
||||||
|
|
||||||
public void SetupUser()
|
public void SetupUser()
|
||||||
{
|
{
|
||||||
User.GetUserRole().Returns(UserRole.User);
|
User.GetUserRole().Returns(UserRole.User);
|
||||||
|
@ -11,7 +11,7 @@ public sealed class CreateTenantCommandValidationTests :
|
|||||||
public CreateTenantCommandValidationTests() : base(new CreateTenantCommandValidation())
|
public CreateTenantCommandValidationTests() : base(new CreateTenantCommandValidation())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Valid()
|
public void Should_Be_Valid()
|
||||||
{
|
{
|
||||||
@ -19,7 +19,7 @@ public sealed class CreateTenantCommandValidationTests :
|
|||||||
|
|
||||||
ShouldBeValid(command);
|
ShouldBeValid(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Invalid_For_Empty_Tenant_Id()
|
public void Should_Be_Invalid_For_Empty_Tenant_Id()
|
||||||
{
|
{
|
||||||
@ -30,7 +30,7 @@ public sealed class CreateTenantCommandValidationTests :
|
|||||||
DomainErrorCodes.Tenant.TenantEmptyId,
|
DomainErrorCodes.Tenant.TenantEmptyId,
|
||||||
"Tenant id may not be empty");
|
"Tenant id may not be empty");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Invalid_For_Empty_Tenant_Name()
|
public void Should_Be_Invalid_For_Empty_Tenant_Name()
|
||||||
{
|
{
|
||||||
@ -46,7 +46,7 @@ public sealed class CreateTenantCommandValidationTests :
|
|||||||
Guid? id = null,
|
Guid? id = null,
|
||||||
string? name = null)
|
string? name = null)
|
||||||
{
|
{
|
||||||
return new(
|
return new CreateTenantCommand(
|
||||||
id ?? Guid.NewGuid(),
|
id ?? Guid.NewGuid(),
|
||||||
name ?? "Test Tenant");
|
name ?? "Test Tenant");
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ public sealed class DeleteTenantCommandHandlerTests
|
|||||||
var tenant = _fixture.SetupTenant();
|
var tenant = _fixture.SetupTenant();
|
||||||
|
|
||||||
var command = new DeleteTenantCommand(tenant.Id);
|
var command = new DeleteTenantCommand(tenant.Id);
|
||||||
|
|
||||||
_fixture.CommandHandler.Handle(command, default).Wait();
|
_fixture.CommandHandler.Handle(command, default).Wait();
|
||||||
|
|
||||||
_fixture
|
_fixture
|
||||||
@ -24,14 +24,14 @@ public sealed class DeleteTenantCommandHandlerTests
|
|||||||
.VerifyCommit()
|
.VerifyCommit()
|
||||||
.VerifyRaisedEvent<TenantDeletedEvent>(x => x.AggregateId == tenant.Id);
|
.VerifyRaisedEvent<TenantDeletedEvent>(x => x.AggregateId == tenant.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Not_Delete_Non_Existing_Tenant()
|
public void Should_Not_Delete_Non_Existing_Tenant()
|
||||||
{
|
{
|
||||||
_fixture.SetupTenant();
|
_fixture.SetupTenant();
|
||||||
|
|
||||||
var command = new DeleteTenantCommand(Guid.NewGuid());
|
var command = new DeleteTenantCommand(Guid.NewGuid());
|
||||||
|
|
||||||
_fixture.CommandHandler.Handle(command, default).Wait();
|
_fixture.CommandHandler.Handle(command, default).Wait();
|
||||||
|
|
||||||
_fixture
|
_fixture
|
||||||
|
@ -7,17 +7,12 @@ namespace CleanArchitecture.Domain.Tests.CommandHandler.Tenant.DeleteTenant;
|
|||||||
|
|
||||||
public sealed class DeleteTenantCommandTestFixture : CommandHandlerFixtureBase
|
public sealed class DeleteTenantCommandTestFixture : CommandHandlerFixtureBase
|
||||||
{
|
{
|
||||||
public DeleteTenantCommandHandler CommandHandler { get;}
|
|
||||||
|
|
||||||
private ITenantRepository TenantRepository { get; }
|
|
||||||
private IUserRepository UserRepository { get; }
|
|
||||||
|
|
||||||
public DeleteTenantCommandTestFixture()
|
public DeleteTenantCommandTestFixture()
|
||||||
{
|
{
|
||||||
TenantRepository = Substitute.For<ITenantRepository>();
|
TenantRepository = Substitute.For<ITenantRepository>();
|
||||||
UserRepository = Substitute.For<IUserRepository>();
|
UserRepository = Substitute.For<IUserRepository>();
|
||||||
|
|
||||||
CommandHandler = new(
|
CommandHandler = new DeleteTenantCommandHandler(
|
||||||
Bus,
|
Bus,
|
||||||
UnitOfWork,
|
UnitOfWork,
|
||||||
NotificationHandler,
|
NotificationHandler,
|
||||||
@ -26,14 +21,19 @@ public sealed class DeleteTenantCommandTestFixture : CommandHandlerFixtureBase
|
|||||||
User);
|
User);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DeleteTenantCommandHandler CommandHandler { get; }
|
||||||
|
|
||||||
|
private ITenantRepository TenantRepository { get; }
|
||||||
|
private IUserRepository UserRepository { get; }
|
||||||
|
|
||||||
public Entities.Tenant SetupTenant()
|
public Entities.Tenant SetupTenant()
|
||||||
{
|
{
|
||||||
var tenant = new Entities.Tenant(Guid.NewGuid(), "TestTenant");
|
var tenant = new Entities.Tenant(Guid.NewGuid(), "TestTenant");
|
||||||
|
|
||||||
TenantRepository
|
TenantRepository
|
||||||
.GetByIdAsync(Arg.Is<Guid>(y => y == tenant.Id))
|
.GetByIdAsync(Arg.Is<Guid>(y => y == tenant.Id))
|
||||||
.Returns(tenant);
|
.Returns(tenant);
|
||||||
|
|
||||||
return tenant;
|
return tenant;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -11,7 +11,7 @@ public sealed class DeleteTenantCommandValidationTests :
|
|||||||
public DeleteTenantCommandValidationTests() : base(new DeleteTenantCommandValidation())
|
public DeleteTenantCommandValidationTests() : base(new DeleteTenantCommandValidation())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Valid()
|
public void Should_Be_Valid()
|
||||||
{
|
{
|
||||||
@ -19,7 +19,7 @@ public sealed class DeleteTenantCommandValidationTests :
|
|||||||
|
|
||||||
ShouldBeValid(command);
|
ShouldBeValid(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Invalid_For_Empty_Tenant_Id()
|
public void Should_Be_Invalid_For_Empty_Tenant_Id()
|
||||||
{
|
{
|
||||||
@ -30,9 +30,9 @@ public sealed class DeleteTenantCommandValidationTests :
|
|||||||
DomainErrorCodes.Tenant.TenantEmptyId,
|
DomainErrorCodes.Tenant.TenantEmptyId,
|
||||||
"Tenant id may not be empty");
|
"Tenant id may not be empty");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static DeleteTenantCommand CreateTestCommand(Guid? tenantId = null)
|
private static DeleteTenantCommand CreateTestCommand(Guid? tenantId = null)
|
||||||
{
|
{
|
||||||
return new(tenantId ?? Guid.NewGuid());
|
return new DeleteTenantCommand(tenantId ?? Guid.NewGuid());
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -16,7 +16,7 @@ public sealed class UpdateTenantCommandHandlerTests
|
|||||||
var command = new UpdateTenantCommand(
|
var command = new UpdateTenantCommand(
|
||||||
Guid.NewGuid(),
|
Guid.NewGuid(),
|
||||||
"Tenant Name");
|
"Tenant Name");
|
||||||
|
|
||||||
_fixture.SetupExistingTenant(command.AggregateId);
|
_fixture.SetupExistingTenant(command.AggregateId);
|
||||||
|
|
||||||
_fixture.CommandHandler.Handle(command, default!).Wait();
|
_fixture.CommandHandler.Handle(command, default!).Wait();
|
||||||
@ -28,14 +28,14 @@ public sealed class UpdateTenantCommandHandlerTests
|
|||||||
x.AggregateId == command.AggregateId &&
|
x.AggregateId == command.AggregateId &&
|
||||||
x.Name == command.Name);
|
x.Name == command.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Not_Update_Tenant_Insufficient_Permissions()
|
public void Should_Not_Update_Tenant_Insufficient_Permissions()
|
||||||
{
|
{
|
||||||
var command = new UpdateTenantCommand(
|
var command = new UpdateTenantCommand(
|
||||||
Guid.NewGuid(),
|
Guid.NewGuid(),
|
||||||
"Tenant Name");
|
"Tenant Name");
|
||||||
|
|
||||||
_fixture.SetupUser();
|
_fixture.SetupUser();
|
||||||
|
|
||||||
_fixture.CommandHandler.Handle(command, default!).Wait();
|
_fixture.CommandHandler.Handle(command, default!).Wait();
|
||||||
@ -48,14 +48,14 @@ public sealed class UpdateTenantCommandHandlerTests
|
|||||||
ErrorCodes.InsufficientPermissions,
|
ErrorCodes.InsufficientPermissions,
|
||||||
$"No permission to update tenant {command.AggregateId}");
|
$"No permission to update tenant {command.AggregateId}");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Not_Update_Tenant_Not_Existing()
|
public void Should_Not_Update_Tenant_Not_Existing()
|
||||||
{
|
{
|
||||||
var command = new UpdateTenantCommand(
|
var command = new UpdateTenantCommand(
|
||||||
Guid.NewGuid(),
|
Guid.NewGuid(),
|
||||||
"Tenant Name");
|
"Tenant Name");
|
||||||
|
|
||||||
_fixture.CommandHandler.Handle(command, default!).Wait();
|
_fixture.CommandHandler.Handle(command, default!).Wait();
|
||||||
|
|
||||||
_fixture
|
_fixture
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using CleanArchitecture.Domain.Commands.Tenants.UpdateTenant;
|
using CleanArchitecture.Domain.Commands.Tenants.UpdateTenant;
|
||||||
using CleanArchitecture.Domain.Enums;
|
using CleanArchitecture.Domain.Enums;
|
||||||
using CleanArchitecture.Domain.Interfaces;
|
|
||||||
using CleanArchitecture.Domain.Interfaces.Repositories;
|
using CleanArchitecture.Domain.Interfaces.Repositories;
|
||||||
using NSubstitute;
|
using NSubstitute;
|
||||||
|
|
||||||
@ -9,22 +8,22 @@ namespace CleanArchitecture.Domain.Tests.CommandHandler.Tenant.UpdateTenant;
|
|||||||
|
|
||||||
public sealed class UpdateTenantCommandTestFixture : CommandHandlerFixtureBase
|
public sealed class UpdateTenantCommandTestFixture : CommandHandlerFixtureBase
|
||||||
{
|
{
|
||||||
public UpdateTenantCommandHandler CommandHandler { get;}
|
|
||||||
|
|
||||||
private ITenantRepository TenantRepository { get; }
|
|
||||||
|
|
||||||
public UpdateTenantCommandTestFixture()
|
public UpdateTenantCommandTestFixture()
|
||||||
{
|
{
|
||||||
TenantRepository = Substitute.For<ITenantRepository>();
|
TenantRepository = Substitute.For<ITenantRepository>();
|
||||||
|
|
||||||
CommandHandler = new(
|
CommandHandler = new UpdateTenantCommandHandler(
|
||||||
Bus,
|
Bus,
|
||||||
UnitOfWork,
|
UnitOfWork,
|
||||||
NotificationHandler,
|
NotificationHandler,
|
||||||
TenantRepository,
|
TenantRepository,
|
||||||
User);
|
User);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public UpdateTenantCommandHandler CommandHandler { get; }
|
||||||
|
|
||||||
|
private ITenantRepository TenantRepository { get; }
|
||||||
|
|
||||||
public void SetupUser()
|
public void SetupUser()
|
||||||
{
|
{
|
||||||
User.GetUserRole().Returns(UserRole.User);
|
User.GetUserRole().Returns(UserRole.User);
|
||||||
|
@ -11,7 +11,7 @@ public sealed class UpdateTenantCommandValidationTests :
|
|||||||
public UpdateTenantCommandValidationTests() : base(new UpdateTenantCommandValidation())
|
public UpdateTenantCommandValidationTests() : base(new UpdateTenantCommandValidation())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Valid()
|
public void Should_Be_Valid()
|
||||||
{
|
{
|
||||||
@ -19,7 +19,7 @@ public sealed class UpdateTenantCommandValidationTests :
|
|||||||
|
|
||||||
ShouldBeValid(command);
|
ShouldBeValid(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Invalid_For_Empty_Tenant_Id()
|
public void Should_Be_Invalid_For_Empty_Tenant_Id()
|
||||||
{
|
{
|
||||||
@ -30,7 +30,7 @@ public sealed class UpdateTenantCommandValidationTests :
|
|||||||
DomainErrorCodes.Tenant.TenantEmptyId,
|
DomainErrorCodes.Tenant.TenantEmptyId,
|
||||||
"Tenant id may not be empty");
|
"Tenant id may not be empty");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Invalid_For_Empty_Tenant_Name()
|
public void Should_Be_Invalid_For_Empty_Tenant_Name()
|
||||||
{
|
{
|
||||||
@ -46,7 +46,7 @@ public sealed class UpdateTenantCommandValidationTests :
|
|||||||
Guid? id = null,
|
Guid? id = null,
|
||||||
string? name = null)
|
string? name = null)
|
||||||
{
|
{
|
||||||
return new(
|
return new UpdateTenantCommand(
|
||||||
id ?? Guid.NewGuid(),
|
id ?? Guid.NewGuid(),
|
||||||
name ?? "Test Tenant");
|
name ?? "Test Tenant");
|
||||||
}
|
}
|
||||||
|
@ -90,7 +90,7 @@ public sealed class ChangePasswordCommandValidationTests :
|
|||||||
private static ChangePasswordCommand CreateTestCommand(
|
private static ChangePasswordCommand CreateTestCommand(
|
||||||
string? password = null, string? newPassword = null)
|
string? password = null, string? newPassword = null)
|
||||||
{
|
{
|
||||||
return new(
|
return new ChangePasswordCommand(
|
||||||
password ?? "z8]tnayvd5FNLU9:]AQm",
|
password ?? "z8]tnayvd5FNLU9:]AQm",
|
||||||
newPassword ?? "z8]tnayvd5FNLU9:]AQw");
|
newPassword ?? "z8]tnayvd5FNLU9:]AQw");
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ public sealed class CreateUserCommandHandlerTests
|
|||||||
{
|
{
|
||||||
// Todo: Fix tests
|
// Todo: Fix tests
|
||||||
_fixture.SetupCurrentUser();
|
_fixture.SetupCurrentUser();
|
||||||
|
|
||||||
var user = _fixture.SetupUser();
|
var user = _fixture.SetupUser();
|
||||||
_fixture.SetupTenant(user.TenantId);
|
_fixture.SetupTenant(user.TenantId);
|
||||||
|
|
||||||
@ -39,7 +39,7 @@ public sealed class CreateUserCommandHandlerTests
|
|||||||
public void Should_Not_Create_Already_Existing_User()
|
public void Should_Not_Create_Already_Existing_User()
|
||||||
{
|
{
|
||||||
_fixture.SetupCurrentUser();
|
_fixture.SetupCurrentUser();
|
||||||
|
|
||||||
var user = _fixture.SetupUser();
|
var user = _fixture.SetupUser();
|
||||||
|
|
||||||
var command = new CreateUserCommand(
|
var command = new CreateUserCommand(
|
||||||
@ -60,12 +60,12 @@ public sealed class CreateUserCommandHandlerTests
|
|||||||
DomainErrorCodes.User.UserAlreadyExists,
|
DomainErrorCodes.User.UserAlreadyExists,
|
||||||
$"There is already a user with Id {command.UserId}");
|
$"There is already a user with Id {command.UserId}");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Not_Create_User_Tenant_Does_Not_Exist()
|
public void Should_Not_Create_User_Tenant_Does_Not_Exist()
|
||||||
{
|
{
|
||||||
_fixture.SetupCurrentUser();
|
_fixture.SetupCurrentUser();
|
||||||
|
|
||||||
_fixture.SetupUser();
|
_fixture.SetupUser();
|
||||||
|
|
||||||
var command = new CreateUserCommand(
|
var command = new CreateUserCommand(
|
||||||
@ -86,7 +86,7 @@ public sealed class CreateUserCommandHandlerTests
|
|||||||
ErrorCodes.ObjectNotFound,
|
ErrorCodes.ObjectNotFound,
|
||||||
$"There is no tenant with Id {command.TenantId}");
|
$"There is no tenant with Id {command.TenantId}");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Not_Create_User_Insufficient_Permissions()
|
public void Should_Not_Create_User_Insufficient_Permissions()
|
||||||
{
|
{
|
||||||
|
@ -176,7 +176,7 @@ public sealed class CreateUserCommandValidationTests :
|
|||||||
|
|
||||||
ShouldHaveSingleError(command, DomainErrorCodes.User.UserLongPassword);
|
ShouldHaveSingleError(command, DomainErrorCodes.User.UserLongPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Invalid_For_Empty_Tenant_Id()
|
public void Should_Be_Invalid_For_Empty_Tenant_Id()
|
||||||
{
|
{
|
||||||
@ -193,7 +193,7 @@ public sealed class CreateUserCommandValidationTests :
|
|||||||
string? lastName = null,
|
string? lastName = null,
|
||||||
string? password = null)
|
string? password = null)
|
||||||
{
|
{
|
||||||
return new(
|
return new CreateUserCommand(
|
||||||
userId ?? Guid.NewGuid(),
|
userId ?? Guid.NewGuid(),
|
||||||
tenantId ?? Guid.NewGuid(),
|
tenantId ?? Guid.NewGuid(),
|
||||||
email ?? "test@email.com",
|
email ?? "test@email.com",
|
||||||
|
@ -33,6 +33,6 @@ public sealed class DeleteUserCommandValidationTests :
|
|||||||
|
|
||||||
private static DeleteUserCommand CreateTestCommand(Guid? userId = null)
|
private static DeleteUserCommand CreateTestCommand(Guid? userId = null)
|
||||||
{
|
{
|
||||||
return new(userId ?? Guid.NewGuid());
|
return new DeleteUserCommand(userId ?? Guid.NewGuid());
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -125,7 +125,7 @@ public sealed class LoginUserCommandValidationTests :
|
|||||||
string? email = null,
|
string? email = null,
|
||||||
string? password = null)
|
string? password = null)
|
||||||
{
|
{
|
||||||
return new(
|
return new LoginUserCommand(
|
||||||
email ?? "test@email.com",
|
email ?? "test@email.com",
|
||||||
password ?? "Po=PF]PC6t.?8?ks)A6W");
|
password ?? "Po=PF]PC6t.?8?ks)A6W");
|
||||||
}
|
}
|
||||||
|
@ -109,7 +109,7 @@ public sealed class UpdateUserCommandValidationTests :
|
|||||||
DomainErrorCodes.User.UserLastNameExceedsMaxLength,
|
DomainErrorCodes.User.UserLastNameExceedsMaxLength,
|
||||||
$"LastName may not be longer than {MaxLengths.User.LastName} characters");
|
$"LastName may not be longer than {MaxLengths.User.LastName} characters");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Be_Invalid_For_Empty_Tenant_Id()
|
public void Should_Be_Invalid_For_Empty_Tenant_Id()
|
||||||
{
|
{
|
||||||
@ -129,7 +129,7 @@ public sealed class UpdateUserCommandValidationTests :
|
|||||||
string? lastName = null,
|
string? lastName = null,
|
||||||
UserRole? role = null)
|
UserRole? role = null)
|
||||||
{
|
{
|
||||||
return new(
|
return new UpdateUserCommand(
|
||||||
userId ?? Guid.NewGuid(),
|
userId ?? Guid.NewGuid(),
|
||||||
email ?? "test@email.com",
|
email ?? "test@email.com",
|
||||||
firstName ?? "test",
|
firstName ?? "test",
|
||||||
|
@ -59,17 +59,20 @@ public sealed class ApiUser : IUser
|
|||||||
{
|
{
|
||||||
return _name;
|
return _name;
|
||||||
}
|
}
|
||||||
|
|
||||||
var identity = _httpContextAccessor.HttpContext?.User.Identity;
|
var identity = _httpContextAccessor.HttpContext?.User.Identity;
|
||||||
if (identity is null)
|
if (identity is null)
|
||||||
{
|
{
|
||||||
_name = string.Empty;
|
_name = string.Empty;
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(identity.Name))
|
if (!string.IsNullOrWhiteSpace(identity.Name))
|
||||||
{
|
{
|
||||||
_name = identity.Name;
|
_name = identity.Name;
|
||||||
return identity.Name;
|
return identity.Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
var claim = _httpContextAccessor.HttpContext!.User.Claims
|
var claim = _httpContextAccessor.HttpContext!.User.Claims
|
||||||
.FirstOrDefault(c => string.Equals(c.Type, ClaimTypes.Name, StringComparison.OrdinalIgnoreCase))?
|
.FirstOrDefault(c => string.Equals(c.Type, ClaimTypes.Name, StringComparison.OrdinalIgnoreCase))?
|
||||||
.Value;
|
.Value;
|
||||||
|
@ -6,14 +6,14 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
|
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3"/>
|
||||||
<PackageReference Include="FluentValidation" Version="11.7.1" />
|
<PackageReference Include="FluentValidation" Version="11.7.1"/>
|
||||||
<PackageReference Include="MediatR" Version="12.1.1" />
|
<PackageReference Include="MediatR" Version="12.1.1"/>
|
||||||
<PackageReference Include="Microsoft.Extensions.Options" Version="7.0.1" />
|
<PackageReference Include="Microsoft.Extensions.Options" Version="7.0.1"/>
|
||||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.32.1" />
|
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.32.1"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
<FrameworkReference Include="Microsoft.AspNetCore.App"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -9,9 +9,9 @@ namespace CleanArchitecture.Domain.Commands;
|
|||||||
|
|
||||||
public abstract class CommandHandlerBase
|
public abstract class CommandHandlerBase
|
||||||
{
|
{
|
||||||
protected readonly IMediatorHandler Bus;
|
|
||||||
private readonly DomainNotificationHandler _notifications;
|
private readonly DomainNotificationHandler _notifications;
|
||||||
private readonly IUnitOfWork _unitOfWork;
|
private readonly IUnitOfWork _unitOfWork;
|
||||||
|
protected readonly IMediatorHandler Bus;
|
||||||
|
|
||||||
protected CommandHandlerBase(
|
protected CommandHandlerBase(
|
||||||
IMediatorHandler bus,
|
IMediatorHandler bus,
|
||||||
|
@ -5,14 +5,14 @@ namespace CleanArchitecture.Domain.Commands.Tenants.CreateTenant;
|
|||||||
public sealed class CreateTenantCommand : CommandBase
|
public sealed class CreateTenantCommand : CommandBase
|
||||||
{
|
{
|
||||||
private static readonly CreateTenantCommandValidation s_validation = new();
|
private static readonly CreateTenantCommandValidation s_validation = new();
|
||||||
|
|
||||||
public string Name { get; }
|
|
||||||
|
|
||||||
public CreateTenantCommand(Guid tenantId, string name) : base(tenantId)
|
public CreateTenantCommand(Guid tenantId, string name) : base(tenantId)
|
||||||
{
|
{
|
||||||
Name = name;
|
Name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string Name { get; }
|
||||||
|
|
||||||
public override bool IsValid()
|
public override bool IsValid()
|
||||||
{
|
{
|
||||||
ValidationResult = s_validation.Validate(this);
|
ValidationResult = s_validation.Validate(this);
|
||||||
|
@ -34,7 +34,7 @@ public sealed class CreateTenantCommandHandler : CommandHandlerBase,
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_user.GetUserRole() != UserRole.Admin)
|
if (_user.GetUserRole() != UserRole.Admin)
|
||||||
{
|
{
|
||||||
await NotifyAsync(
|
await NotifyAsync(
|
||||||
@ -60,7 +60,7 @@ public sealed class CreateTenantCommandHandler : CommandHandlerBase,
|
|||||||
var tenant = new Tenant(
|
var tenant = new Tenant(
|
||||||
request.AggregateId,
|
request.AggregateId,
|
||||||
request.Name);
|
request.Name);
|
||||||
|
|
||||||
_tenantRepository.Add(tenant);
|
_tenantRepository.Add(tenant);
|
||||||
|
|
||||||
if (await CommitAsync())
|
if (await CommitAsync())
|
||||||
|
@ -11,7 +11,7 @@ public sealed class CreateTenantCommandValidation : AbstractValidator<CreateTena
|
|||||||
AddRuleForId();
|
AddRuleForId();
|
||||||
AddRuleForName();
|
AddRuleForName();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddRuleForId()
|
private void AddRuleForId()
|
||||||
{
|
{
|
||||||
RuleFor(cmd => cmd.AggregateId)
|
RuleFor(cmd => cmd.AggregateId)
|
||||||
@ -19,7 +19,7 @@ public sealed class CreateTenantCommandValidation : AbstractValidator<CreateTena
|
|||||||
.WithErrorCode(DomainErrorCodes.Tenant.TenantEmptyId)
|
.WithErrorCode(DomainErrorCodes.Tenant.TenantEmptyId)
|
||||||
.WithMessage("Tenant id may not be empty");
|
.WithMessage("Tenant id may not be empty");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddRuleForName()
|
private void AddRuleForName()
|
||||||
{
|
{
|
||||||
RuleFor(cmd => cmd.Name)
|
RuleFor(cmd => cmd.Name)
|
||||||
|
@ -5,7 +5,7 @@ namespace CleanArchitecture.Domain.Commands.Tenants.DeleteTenant;
|
|||||||
public sealed class DeleteTenantCommand : CommandBase
|
public sealed class DeleteTenantCommand : CommandBase
|
||||||
{
|
{
|
||||||
private static readonly DeleteTenantCommandValidation s_validation = new();
|
private static readonly DeleteTenantCommandValidation s_validation = new();
|
||||||
|
|
||||||
public DeleteTenantCommand(Guid tenantId) : base(tenantId)
|
public DeleteTenantCommand(Guid tenantId) : base(tenantId)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -15,8 +15,8 @@ public sealed class DeleteTenantCommandHandler : CommandHandlerBase,
|
|||||||
IRequestHandler<DeleteTenantCommand>
|
IRequestHandler<DeleteTenantCommand>
|
||||||
{
|
{
|
||||||
private readonly ITenantRepository _tenantRepository;
|
private readonly ITenantRepository _tenantRepository;
|
||||||
private readonly IUserRepository _userRepository;
|
|
||||||
private readonly IUser _user;
|
private readonly IUser _user;
|
||||||
|
private readonly IUserRepository _userRepository;
|
||||||
|
|
||||||
public DeleteTenantCommandHandler(
|
public DeleteTenantCommandHandler(
|
||||||
IMediatorHandler bus,
|
IMediatorHandler bus,
|
||||||
@ -37,7 +37,7 @@ public sealed class DeleteTenantCommandHandler : CommandHandlerBase,
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Todo: Test following
|
// Todo: Test following
|
||||||
|
|
||||||
if (_user.GetUserRole() != UserRole.Admin)
|
if (_user.GetUserRole() != UserRole.Admin)
|
||||||
@ -67,9 +67,9 @@ public sealed class DeleteTenantCommandHandler : CommandHandlerBase,
|
|||||||
var tenantUsers = _userRepository
|
var tenantUsers = _userRepository
|
||||||
.GetAll()
|
.GetAll()
|
||||||
.Where(x => x.TenantId == request.AggregateId);
|
.Where(x => x.TenantId == request.AggregateId);
|
||||||
|
|
||||||
_userRepository.RemoveRange(tenantUsers);
|
_userRepository.RemoveRange(tenantUsers);
|
||||||
|
|
||||||
_tenantRepository.Remove(tenant);
|
_tenantRepository.Remove(tenant);
|
||||||
|
|
||||||
if (await CommitAsync())
|
if (await CommitAsync())
|
||||||
|
@ -9,7 +9,7 @@ public sealed class DeleteTenantCommandValidation : AbstractValidator<DeleteTena
|
|||||||
{
|
{
|
||||||
AddRuleForId();
|
AddRuleForId();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddRuleForId()
|
private void AddRuleForId()
|
||||||
{
|
{
|
||||||
RuleFor(cmd => cmd.AggregateId)
|
RuleFor(cmd => cmd.AggregateId)
|
||||||
|
@ -5,14 +5,14 @@ namespace CleanArchitecture.Domain.Commands.Tenants.UpdateTenant;
|
|||||||
public sealed class UpdateTenantCommand : CommandBase
|
public sealed class UpdateTenantCommand : CommandBase
|
||||||
{
|
{
|
||||||
private static readonly UpdateTenantCommandValidation s_validation = new();
|
private static readonly UpdateTenantCommandValidation s_validation = new();
|
||||||
|
|
||||||
public string Name { get; }
|
|
||||||
|
|
||||||
public UpdateTenantCommand(Guid tenantId, string name) : base(tenantId)
|
public UpdateTenantCommand(Guid tenantId, string name) : base(tenantId)
|
||||||
{
|
{
|
||||||
Name = name;
|
Name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string Name { get; }
|
||||||
|
|
||||||
public override bool IsValid()
|
public override bool IsValid()
|
||||||
{
|
{
|
||||||
ValidationResult = s_validation.Validate(this);
|
ValidationResult = s_validation.Validate(this);
|
||||||
|
@ -33,7 +33,7 @@ public sealed class UpdateTenantCommandHandler : CommandHandlerBase,
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_user.GetUserRole() != UserRole.Admin)
|
if (_user.GetUserRole() != UserRole.Admin)
|
||||||
{
|
{
|
||||||
await NotifyAsync(
|
await NotifyAsync(
|
||||||
@ -57,9 +57,9 @@ public sealed class UpdateTenantCommandHandler : CommandHandlerBase,
|
|||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
tenant.SetName(request.Name);
|
tenant.SetName(request.Name);
|
||||||
|
|
||||||
if (await CommitAsync())
|
if (await CommitAsync())
|
||||||
{
|
{
|
||||||
await Bus.RaiseEventAsync(new TenantUpdatedEvent(
|
await Bus.RaiseEventAsync(new TenantUpdatedEvent(
|
||||||
|
@ -11,7 +11,7 @@ public sealed class UpdateTenantCommandValidation : AbstractValidator<UpdateTena
|
|||||||
AddRuleForId();
|
AddRuleForId();
|
||||||
AddRuleForName();
|
AddRuleForName();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddRuleForId()
|
private void AddRuleForId()
|
||||||
{
|
{
|
||||||
RuleFor(cmd => cmd.AggregateId)
|
RuleFor(cmd => cmd.AggregateId)
|
||||||
@ -19,7 +19,7 @@ public sealed class UpdateTenantCommandValidation : AbstractValidator<UpdateTena
|
|||||||
.WithErrorCode(DomainErrorCodes.Tenant.TenantEmptyId)
|
.WithErrorCode(DomainErrorCodes.Tenant.TenantEmptyId)
|
||||||
.WithMessage("Tenant id may not be empty");
|
.WithMessage("Tenant id may not be empty");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddRuleForName()
|
private void AddRuleForName()
|
||||||
{
|
{
|
||||||
RuleFor(cmd => cmd.Name)
|
RuleFor(cmd => cmd.Name)
|
||||||
|
@ -15,9 +15,9 @@ namespace CleanArchitecture.Domain.Commands.Users.CreateUser;
|
|||||||
public sealed class CreateUserCommandHandler : CommandHandlerBase,
|
public sealed class CreateUserCommandHandler : CommandHandlerBase,
|
||||||
IRequestHandler<CreateUserCommand>
|
IRequestHandler<CreateUserCommand>
|
||||||
{
|
{
|
||||||
private readonly IUserRepository _userRepository;
|
|
||||||
private readonly ITenantRepository _tenantRepository;
|
private readonly ITenantRepository _tenantRepository;
|
||||||
private readonly IUser _user;
|
private readonly IUser _user;
|
||||||
|
private readonly IUserRepository _userRepository;
|
||||||
|
|
||||||
public CreateUserCommandHandler(
|
public CreateUserCommandHandler(
|
||||||
IMediatorHandler bus,
|
IMediatorHandler bus,
|
||||||
@ -38,9 +38,9 @@ public sealed class CreateUserCommandHandler : CommandHandlerBase,
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var currentUser = await _userRepository.GetByIdAsync(_user.GetUserId());
|
var currentUser = await _userRepository.GetByIdAsync(_user.GetUserId());
|
||||||
|
|
||||||
if (currentUser is null || currentUser.Role != UserRole.Admin)
|
if (currentUser is null || currentUser.Role != UserRole.Admin)
|
||||||
{
|
{
|
||||||
await NotifyAsync(
|
await NotifyAsync(
|
||||||
|
@ -75,7 +75,7 @@ public sealed class UpdateUserCommandHandler : CommandHandlerBase,
|
|||||||
if (_user.GetUserRole() == UserRole.Admin)
|
if (_user.GetUserRole() == UserRole.Admin)
|
||||||
{
|
{
|
||||||
user.SetRole(request.Role);
|
user.SetRole(request.Role);
|
||||||
|
|
||||||
// Todo: Test
|
// Todo: Test
|
||||||
// Todo: Check if tenant exists first
|
// Todo: Check if tenant exists first
|
||||||
user.SetTenant(request.TenantId);
|
user.SetTenant(request.TenantId);
|
||||||
|
@ -23,7 +23,7 @@ public sealed class UpdateUserCommandValidation : AbstractValidator<UpdateUserCo
|
|||||||
.WithErrorCode(DomainErrorCodes.User.UserEmptyId)
|
.WithErrorCode(DomainErrorCodes.User.UserEmptyId)
|
||||||
.WithMessage("User id may not be empty");
|
.WithMessage("User id may not be empty");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddRuleForTenantId()
|
private void AddRuleForTenantId()
|
||||||
{
|
{
|
||||||
RuleFor(cmd => cmd.TenantId)
|
RuleFor(cmd => cmd.TenantId)
|
||||||
|
@ -19,4 +19,4 @@ public abstract class Message : IRequest
|
|||||||
|
|
||||||
public Guid AggregateId { get; private set; }
|
public Guid AggregateId { get; private set; }
|
||||||
public string MessageType { get; protected set; }
|
public string MessageType { get; protected set; }
|
||||||
}
|
}
|
@ -4,11 +4,6 @@ namespace CleanArchitecture.Domain.DomainEvents;
|
|||||||
|
|
||||||
public class StoredDomainEvent : DomainEvent
|
public class StoredDomainEvent : DomainEvent
|
||||||
{
|
{
|
||||||
public Guid Id { get; private set; }
|
|
||||||
public string Data { get; private set; } = string.Empty;
|
|
||||||
public string User { get; private set; } = string.Empty;
|
|
||||||
public string CorrelationId { get; private set; } = string.Empty;
|
|
||||||
|
|
||||||
public StoredDomainEvent(
|
public StoredDomainEvent(
|
||||||
DomainEvent domainEvent,
|
DomainEvent domainEvent,
|
||||||
string data,
|
string data,
|
||||||
@ -24,5 +19,11 @@ public class StoredDomainEvent : DomainEvent
|
|||||||
|
|
||||||
// EF Constructor
|
// EF Constructor
|
||||||
protected StoredDomainEvent() : base(Guid.NewGuid())
|
protected StoredDomainEvent() : base(Guid.NewGuid())
|
||||||
{ }
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Guid Id { get; private set; }
|
||||||
|
public string Data { get; private set; } = string.Empty;
|
||||||
|
public string User { get; private set; } = string.Empty;
|
||||||
|
public string CorrelationId { get; private set; } = string.Empty;
|
||||||
|
}
|
@ -5,21 +5,16 @@ namespace CleanArchitecture.Domain.DomainNotifications;
|
|||||||
|
|
||||||
public class StoredDomainNotification : DomainNotification
|
public class StoredDomainNotification : DomainNotification
|
||||||
{
|
{
|
||||||
public Guid Id { get; private set; }
|
|
||||||
public string SerializedData { get; private set; } = string.Empty;
|
|
||||||
public string User { get; private set; } = string.Empty;
|
|
||||||
public string CorrelationId { get; private set; } = string.Empty;
|
|
||||||
|
|
||||||
public StoredDomainNotification(
|
public StoredDomainNotification(
|
||||||
DomainNotification domainNotification,
|
DomainNotification domainNotification,
|
||||||
string data,
|
string data,
|
||||||
string user,
|
string user,
|
||||||
string correlationId) : base(
|
string correlationId) : base(
|
||||||
domainNotification.Key,
|
domainNotification.Key,
|
||||||
domainNotification.Value,
|
domainNotification.Value,
|
||||||
domainNotification.Code,
|
domainNotification.Code,
|
||||||
null,
|
null,
|
||||||
domainNotification.AggregateId)
|
domainNotification.AggregateId)
|
||||||
{
|
{
|
||||||
Id = Guid.NewGuid();
|
Id = Guid.NewGuid();
|
||||||
User = user;
|
User = user;
|
||||||
@ -31,5 +26,11 @@ public class StoredDomainNotification : DomainNotification
|
|||||||
|
|
||||||
// EF Constructor
|
// EF Constructor
|
||||||
protected StoredDomainNotification() : base(string.Empty, string.Empty, string.Empty)
|
protected StoredDomainNotification() : base(string.Empty, string.Empty, string.Empty)
|
||||||
{ }
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Guid Id { get; private set; }
|
||||||
|
public string SerializedData { get; private set; } = string.Empty;
|
||||||
|
public string User { get; private set; } = string.Empty;
|
||||||
|
public string CorrelationId { get; private set; } = string.Empty;
|
||||||
|
}
|
@ -5,17 +5,17 @@ namespace CleanArchitecture.Domain.Entities;
|
|||||||
|
|
||||||
public class Tenant : Entity
|
public class Tenant : Entity
|
||||||
{
|
{
|
||||||
public string Name { get; private set; }
|
|
||||||
|
|
||||||
public virtual ICollection<User> Users { get; private set; } = new HashSet<User>();
|
|
||||||
|
|
||||||
public Tenant(
|
public Tenant(
|
||||||
Guid id,
|
Guid id,
|
||||||
string name) : base(id)
|
string name) : base(id)
|
||||||
{
|
{
|
||||||
Name = name;
|
Name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string Name { get; private set; }
|
||||||
|
|
||||||
|
public virtual ICollection<User> Users { get; private set; } = new HashSet<User>();
|
||||||
|
|
||||||
public void SetName(string name)
|
public void SetName(string name)
|
||||||
{
|
{
|
||||||
Name = name;
|
Name = name;
|
||||||
|
@ -1,22 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using CleanArchitecture.Domain.Enums;
|
using CleanArchitecture.Domain.Enums;
|
||||||
|
|
||||||
namespace CleanArchitecture.Domain.Entities;
|
namespace CleanArchitecture.Domain.Entities;
|
||||||
|
|
||||||
public class User : Entity
|
public class User : Entity
|
||||||
{
|
{
|
||||||
public string Email { get; private set; }
|
|
||||||
public string FirstName { get; private set; }
|
|
||||||
public string LastName { get; private set; }
|
|
||||||
public string Password { get; private set; }
|
|
||||||
public UserRole Role { get; private set; }
|
|
||||||
|
|
||||||
public string FullName => $"{FirstName}, {LastName}";
|
|
||||||
|
|
||||||
public Guid TenantId { get; private set; }
|
|
||||||
public virtual Tenant Tenant { get; private set; } = null!;
|
|
||||||
|
|
||||||
public User(
|
public User(
|
||||||
Guid id,
|
Guid id,
|
||||||
Guid tenantId,
|
Guid tenantId,
|
||||||
@ -34,6 +22,17 @@ public class User : Entity
|
|||||||
Role = role;
|
Role = role;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string Email { get; private set; }
|
||||||
|
public string FirstName { get; private set; }
|
||||||
|
public string LastName { get; private set; }
|
||||||
|
public string Password { get; private set; }
|
||||||
|
public UserRole Role { get; private set; }
|
||||||
|
|
||||||
|
public string FullName => $"{FirstName}, {LastName}";
|
||||||
|
|
||||||
|
public Guid TenantId { get; private set; }
|
||||||
|
public virtual Tenant Tenant { get; private set; } = null!;
|
||||||
|
|
||||||
public void SetEmail(string email)
|
public void SetEmail(string email)
|
||||||
{
|
{
|
||||||
Email = email;
|
Email = email;
|
||||||
@ -58,7 +57,7 @@ public class User : Entity
|
|||||||
{
|
{
|
||||||
Role = role;
|
Role = role;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetTenant(Guid tenantId)
|
public void SetTenant(Guid tenantId)
|
||||||
{
|
{
|
||||||
TenantId = tenantId;
|
TenantId = tenantId;
|
||||||
|
@ -5,10 +5,10 @@ namespace CleanArchitecture.Domain.Events.Tenant;
|
|||||||
|
|
||||||
public sealed class TenantCreatedEvent : DomainEvent
|
public sealed class TenantCreatedEvent : DomainEvent
|
||||||
{
|
{
|
||||||
public string Name { get; set; }
|
|
||||||
|
|
||||||
public TenantCreatedEvent(Guid tenantId, string name) : base(tenantId)
|
public TenantCreatedEvent(Guid tenantId, string name) : base(tenantId)
|
||||||
{
|
{
|
||||||
Name = name;
|
Name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string Name { get; set; }
|
||||||
}
|
}
|
@ -5,10 +5,10 @@ namespace CleanArchitecture.Domain.Events.Tenant;
|
|||||||
|
|
||||||
public sealed class TenantUpdatedEvent : DomainEvent
|
public sealed class TenantUpdatedEvent : DomainEvent
|
||||||
{
|
{
|
||||||
public string Name { get; set; }
|
|
||||||
|
|
||||||
public TenantUpdatedEvent(Guid tenantId, string name) : base(tenantId)
|
public TenantUpdatedEvent(Guid tenantId, string name) : base(tenantId)
|
||||||
{
|
{
|
||||||
Name = name;
|
Name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string Name { get; set; }
|
||||||
}
|
}
|
@ -41,7 +41,7 @@ public static class ServiceCollectionExtension
|
|||||||
services.AddScoped<INotificationHandler<UserUpdatedEvent>, UserEventHandler>();
|
services.AddScoped<INotificationHandler<UserUpdatedEvent>, UserEventHandler>();
|
||||||
services.AddScoped<INotificationHandler<UserDeletedEvent>, UserEventHandler>();
|
services.AddScoped<INotificationHandler<UserDeletedEvent>, UserEventHandler>();
|
||||||
services.AddScoped<INotificationHandler<PasswordChangedEvent>, UserEventHandler>();
|
services.AddScoped<INotificationHandler<PasswordChangedEvent>, UserEventHandler>();
|
||||||
|
|
||||||
// Tenant
|
// Tenant
|
||||||
services.AddScoped<INotificationHandler<TenantCreatedEvent>, TenantEventHandler>();
|
services.AddScoped<INotificationHandler<TenantCreatedEvent>, TenantEventHandler>();
|
||||||
services.AddScoped<INotificationHandler<TenantUpdatedEvent>, TenantEventHandler>();
|
services.AddScoped<INotificationHandler<TenantUpdatedEvent>, TenantEventHandler>();
|
||||||
|
@ -8,10 +8,10 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="FluentAssertions" Version="6.11.0" />
|
<PackageReference Include="FluentAssertions" Version="6.11.0"/>
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.0"/>
|
||||||
<PackageReference Include="NSubstitute" Version="5.0.0" />
|
<PackageReference Include="NSubstitute" Version="5.0.0"/>
|
||||||
<PackageReference Include="xunit" Version="2.5.0" />
|
<PackageReference Include="xunit" Version="2.5.0"/>
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
@ -23,7 +23,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\CleanArchitecture.Infrastructure\CleanArchitecture.Infrastructure.csproj" />
|
<ProjectReference Include="..\CleanArchitecture.Infrastructure\CleanArchitecture.Infrastructure.csproj"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -6,19 +6,19 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj" />
|
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="MediatR" Version="12.1.1" />
|
<PackageReference Include="MediatR" Version="12.1.1"/>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.10" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.10"/>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="7.0.10" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="7.0.10"/>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.10" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.10"/>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.10">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.10">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -22,4 +22,4 @@ public sealed class StoredDomainEventConfiguration : IEntityTypeConfiguration<St
|
|||||||
.HasMaxLength(100)
|
.HasMaxLength(100)
|
||||||
.HasColumnType("nvarchar(100)");
|
.HasColumnType("nvarchar(100)");
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -39,4 +39,4 @@ public sealed class StoredDomainNotificationConfiguration : IEntityTypeConfigura
|
|||||||
builder.Property(c => c.SerializedData)
|
builder.Property(c => c.SerializedData)
|
||||||
.HasColumnName("Data");
|
.HasColumnName("Data");
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,4 +1,3 @@
|
|||||||
using System;
|
|
||||||
using CleanArchitecture.Domain.Constants;
|
using CleanArchitecture.Domain.Constants;
|
||||||
using CleanArchitecture.Domain.Entities;
|
using CleanArchitecture.Domain.Entities;
|
||||||
using CleanArchitecture.Domain.Enums;
|
using CleanArchitecture.Domain.Enums;
|
||||||
|
@ -18,4 +18,4 @@ public class EventStoreDbContext : DbContext
|
|||||||
|
|
||||||
base.OnModelCreating(modelBuilder);
|
base.OnModelCreating(modelBuilder);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -9,9 +9,9 @@ namespace CleanArchitecture.Infrastructure.EventSourcing;
|
|||||||
|
|
||||||
public sealed class DomainEventStore : IDomainEventStore
|
public sealed class DomainEventStore : IDomainEventStore
|
||||||
{
|
{
|
||||||
private readonly EventStoreDbContext _eventStoreDbContext;
|
|
||||||
private readonly DomainNotificationStoreDbContext _domainNotificationStoreDbContext;
|
|
||||||
private readonly IEventStoreContext _context;
|
private readonly IEventStoreContext _context;
|
||||||
|
private readonly DomainNotificationStoreDbContext _domainNotificationStoreDbContext;
|
||||||
|
private readonly EventStoreDbContext _eventStoreDbContext;
|
||||||
|
|
||||||
public DomainEventStore(
|
public DomainEventStore(
|
||||||
EventStoreDbContext eventStoreDbContext,
|
EventStoreDbContext eventStoreDbContext,
|
||||||
@ -53,4 +53,4 @@ public sealed class DomainEventStore : IDomainEventStore
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -14,7 +14,8 @@ public sealed class EventStoreContext : IEventStoreContext
|
|||||||
_user = user;
|
_user = user;
|
||||||
|
|
||||||
if (httpContextAccessor?.HttpContext is null ||
|
if (httpContextAccessor?.HttpContext is null ||
|
||||||
!httpContextAccessor.HttpContext.Request.Headers.TryGetValue("X-CLEAN-ARCHITECTURE-CORRELATION-ID", out var id))
|
!httpContextAccessor.HttpContext.Request.Headers.TryGetValue("X-CLEAN-ARCHITECTURE-CORRELATION-ID",
|
||||||
|
out var id))
|
||||||
{
|
{
|
||||||
_correlationId = $"internal - {Guid.NewGuid()}";
|
_correlationId = $"internal - {Guid.NewGuid()}";
|
||||||
}
|
}
|
||||||
@ -24,7 +25,13 @@ public sealed class EventStoreContext : IEventStoreContext
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetCorrelationId() => _correlationId;
|
public string GetCorrelationId()
|
||||||
|
{
|
||||||
|
return _correlationId;
|
||||||
|
}
|
||||||
|
|
||||||
public string GetUserEmail() => _user?.GetUserEmail() ?? string.Empty;
|
public string GetUserEmail()
|
||||||
}
|
{
|
||||||
|
return _user?.GetUserEmail() ?? string.Empty;
|
||||||
|
}
|
||||||
|
}
|
@ -4,4 +4,4 @@ public interface IEventStoreContext
|
|||||||
{
|
{
|
||||||
public string GetUserEmail();
|
public string GetUserEmail();
|
||||||
public string GetCorrelationId();
|
public string GetCorrelationId();
|
||||||
}
|
}
|
@ -16,18 +16,18 @@ public static class ServiceCollectionExtensions
|
|||||||
{
|
{
|
||||||
public static IServiceCollection AddInfrastructure(
|
public static IServiceCollection AddInfrastructure(
|
||||||
this IServiceCollection services,
|
this IServiceCollection services,
|
||||||
IConfiguration configuration,
|
IConfiguration configuration,
|
||||||
string migrationsAssemblyName,
|
string migrationsAssemblyName,
|
||||||
string connectionStringName = "DefaultConnection")
|
string connectionStringName = "DefaultConnection")
|
||||||
{
|
{
|
||||||
// Add event store db context
|
// Add event store db context
|
||||||
services.AddDbContext<EventStoreDbContext>(
|
services.AddDbContext<EventStoreDbContext>(
|
||||||
options =>
|
options =>
|
||||||
{
|
{
|
||||||
options.UseSqlServer(
|
options.UseSqlServer(
|
||||||
configuration.GetConnectionString(connectionStringName),
|
configuration.GetConnectionString(connectionStringName),
|
||||||
b => b.MigrationsAssembly(migrationsAssemblyName));
|
b => b.MigrationsAssembly(migrationsAssemblyName));
|
||||||
});
|
});
|
||||||
|
|
||||||
services.AddDbContext<DomainNotificationStoreDbContext>(
|
services.AddDbContext<DomainNotificationStoreDbContext>(
|
||||||
options =>
|
options =>
|
||||||
|
@ -5,6 +5,12 @@
|
|||||||
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
[assembly: SuppressMessage("Style", "IDE0161:Convert to file-scoped namespace", Justification = "<Pending>", Scope = "namespace", Target = "~N:CleanArchitecture.Infrastructure.Migrations")]
|
[assembly:
|
||||||
[assembly: SuppressMessage("Style", "IDE0161:Convert to file-scoped namespace", Justification = "<Pending>", Scope = "namespace", Target = "~N:CleanArchitecture.Infrastructure.Migrations.EventStoreDb")]
|
SuppressMessage("Style", "IDE0161:Convert to file-scoped namespace", Justification = "<Pending>",
|
||||||
[assembly: SuppressMessage("Style", "IDE0161:Convert to file-scoped namespace", Justification = "<Pending>", Scope = "namespace", Target = "~N:CleanArchitecture.Infrastructure.Migrations.DomainNotificationStoreDb")]
|
Scope = "namespace", Target = "~N:CleanArchitecture.Infrastructure.Migrations")]
|
||||||
|
[assembly:
|
||||||
|
SuppressMessage("Style", "IDE0161:Convert to file-scoped namespace", Justification = "<Pending>",
|
||||||
|
Scope = "namespace", Target = "~N:CleanArchitecture.Infrastructure.Migrations.EventStoreDb")]
|
||||||
|
[assembly:
|
||||||
|
SuppressMessage("Style", "IDE0161:Convert to file-scoped namespace", Justification = "<Pending>",
|
||||||
|
Scope = "namespace", Target = "~N:CleanArchitecture.Infrastructure.Migrations.DomainNotificationStoreDb")]
|
@ -8,8 +8,8 @@ namespace CleanArchitecture.Infrastructure;
|
|||||||
|
|
||||||
public sealed class InMemoryBus : IMediatorHandler
|
public sealed class InMemoryBus : IMediatorHandler
|
||||||
{
|
{
|
||||||
private readonly IMediator _mediator;
|
|
||||||
private readonly IDomainEventStore _domainEventStore;
|
private readonly IDomainEventStore _domainEventStore;
|
||||||
|
private readonly IMediator _mediator;
|
||||||
|
|
||||||
public InMemoryBus(
|
public InMemoryBus(
|
||||||
IMediator mediator,
|
IMediator mediator,
|
||||||
|
@ -1,43 +1,39 @@
|
|||||||
using System;
|
#nullable disable
|
||||||
|
|
||||||
|
using System;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
#nullable disable
|
namespace CleanArchitecture.Infrastructure.Migrations.DomainNotificationStoreDb;
|
||||||
|
|
||||||
namespace CleanArchitecture.Infrastructure.Migrations.DomainNotificationStoreDb
|
/// <inheritdoc />
|
||||||
|
public partial class AddDomainNotificationStore : Migration
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public partial class AddDomainNotificationStore : Migration
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
migrationBuilder.CreateTable(
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
"StoredDomainNotifications",
|
||||||
{
|
table => new
|
||||||
migrationBuilder.CreateTable(
|
{
|
||||||
name: "StoredDomainNotifications",
|
Id = table.Column<Guid>("uniqueidentifier", nullable: false),
|
||||||
columns: table => new
|
Data = table.Column<string>("nvarchar(max)", nullable: false),
|
||||||
{
|
User = table.Column<string>("nvarchar(100)", maxLength: 100, nullable: false),
|
||||||
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
CorrelationId = table.Column<string>("nvarchar(100)", maxLength: 100, nullable: false),
|
||||||
Data = table.Column<string>(type: "nvarchar(max)", nullable: false),
|
AggregateId = table.Column<Guid>("uniqueidentifier", nullable: false),
|
||||||
User = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
|
MessageType = table.Column<string>("nvarchar(100)", maxLength: 100, nullable: false),
|
||||||
CorrelationId = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
|
Timestamp = table.Column<DateTime>("datetime2", nullable: false),
|
||||||
AggregateId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
Key = table.Column<string>("nvarchar(100)", maxLength: 100, nullable: false),
|
||||||
MessageType = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
|
Value = table.Column<string>("nvarchar(1024)", maxLength: 1024, nullable: false),
|
||||||
Timestamp = table.Column<DateTime>(type: "datetime2", nullable: false),
|
Code = table.Column<string>("nvarchar(100)", maxLength: 100, nullable: false),
|
||||||
Key = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
|
Version = table.Column<int>("int", nullable: false)
|
||||||
Value = table.Column<string>(type: "nvarchar(1024)", maxLength: 1024, nullable: false),
|
},
|
||||||
Code = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
|
constraints: table => { table.PrimaryKey("PK_StoredDomainNotifications", x => x.Id); });
|
||||||
Version = table.Column<int>(type: "int", nullable: false)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_StoredDomainNotifications", x => x.Id);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "StoredDomainNotifications");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
"StoredDomainNotifications");
|
||||||
|
}
|
||||||
|
}
|
@ -1,39 +1,35 @@
|
|||||||
using System;
|
#nullable disable
|
||||||
|
|
||||||
|
using System;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
#nullable disable
|
namespace CleanArchitecture.Infrastructure.Migrations.EventStoreDb;
|
||||||
|
|
||||||
namespace CleanArchitecture.Infrastructure.Migrations.EventStoreDb
|
/// <inheritdoc />
|
||||||
|
public partial class AddEventStore : Migration
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public partial class AddEventStore : Migration
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
migrationBuilder.CreateTable(
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
"StoredDomainEvents",
|
||||||
{
|
table => new
|
||||||
migrationBuilder.CreateTable(
|
{
|
||||||
name: "StoredDomainEvents",
|
Id = table.Column<Guid>("uniqueidentifier", nullable: false),
|
||||||
columns: table => new
|
Data = table.Column<string>("nvarchar(max)", nullable: false),
|
||||||
{
|
User = table.Column<string>("nvarchar(100)", maxLength: 100, nullable: false),
|
||||||
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
CorrelationId = table.Column<string>("nvarchar(100)", maxLength: 100, nullable: false),
|
||||||
Data = table.Column<string>(type: "nvarchar(max)", nullable: false),
|
AggregateId = table.Column<Guid>("uniqueidentifier", nullable: false),
|
||||||
User = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
|
Action = table.Column<string>("varchar(100)", nullable: false),
|
||||||
CorrelationId = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
|
CreationDate = table.Column<DateTime>("datetime2", nullable: false)
|
||||||
AggregateId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
},
|
||||||
Action = table.Column<string>(type: "varchar(100)", nullable: false),
|
constraints: table => { table.PrimaryKey("PK_StoredDomainEvents", x => x.Id); });
|
||||||
CreationDate = table.Column<DateTime>(type: "datetime2", nullable: false)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_StoredDomainEvents", x => x.Id);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "StoredDomainEvents");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
"StoredDomainEvents");
|
||||||
|
}
|
||||||
|
}
|
@ -72,7 +72,7 @@ public class BaseRepository<TEntity> : IRepository<TEntity> where TEntity : Enti
|
|||||||
DbSet.Update(entity);
|
DbSet.Update(entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveRange(IEnumerable<TEntity> entities, bool hardDelete = false)
|
public void RemoveRange(IEnumerable<TEntity> entities, bool hardDelete = false)
|
||||||
{
|
{
|
||||||
if (hardDelete)
|
if (hardDelete)
|
||||||
|
@ -8,14 +8,14 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="FluentAssertions" Version="6.11.0" />
|
<PackageReference Include="FluentAssertions" Version="6.11.0"/>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="7.0.10" />
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="7.0.10"/>
|
||||||
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="7.0.10" />
|
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="7.0.10"/>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="7.0.10" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="7.0.10"/>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.10" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.10"/>
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.0"/>
|
||||||
<PackageReference Include="xunit" Version="2.5.0" />
|
<PackageReference Include="xunit" Version="2.5.0"/>
|
||||||
<PackageReference Include="Xunit.Priority" Version="1.1.6" />
|
<PackageReference Include="Xunit.Priority" Version="1.1.6"/>
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
@ -27,8 +27,8 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\CleanArchitecture.Api\CleanArchitecture.Api.csproj" />
|
<ProjectReference Include="..\CleanArchitecture.Api\CleanArchitecture.Api.csproj"/>
|
||||||
<ProjectReference Include="..\CleanArchitecture.Infrastructure\CleanArchitecture.Infrastructure.csproj" />
|
<ProjectReference Include="..\CleanArchitecture.Infrastructure\CleanArchitecture.Infrastructure.csproj"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -30,17 +30,17 @@ public sealed class TenantControllerTests : IClassFixture<TenantTestFixture>
|
|||||||
await _fixture.AuthenticateUserAsync();
|
await _fixture.AuthenticateUserAsync();
|
||||||
|
|
||||||
var response = await _fixture.ServerClient.GetAsync($"/api/v1/Tenant/{_fixture.CreatedTenantId}");
|
var response = await _fixture.ServerClient.GetAsync($"/api/v1/Tenant/{_fixture.CreatedTenantId}");
|
||||||
|
|
||||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||||
|
|
||||||
var message = await response.Content.ReadAsJsonAsync<TenantViewModel>();
|
var message = await response.Content.ReadAsJsonAsync<TenantViewModel>();
|
||||||
|
|
||||||
message?.Data.Should().NotBeNull();
|
message?.Data.Should().NotBeNull();
|
||||||
|
|
||||||
message!.Data!.Id.Should().Be(_fixture.CreatedTenantId);
|
message!.Data!.Id.Should().Be(_fixture.CreatedTenantId);
|
||||||
message.Data.Name.Should().Be("Test Tenant");
|
message.Data.Name.Should().Be("Test Tenant");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
[Priority(5)]
|
[Priority(5)]
|
||||||
public async Task Should_Get_All_Tenants()
|
public async Task Should_Get_All_Tenants()
|
||||||
@ -48,89 +48,89 @@ public sealed class TenantControllerTests : IClassFixture<TenantTestFixture>
|
|||||||
await _fixture.AuthenticateUserAsync();
|
await _fixture.AuthenticateUserAsync();
|
||||||
|
|
||||||
var response = await _fixture.ServerClient.GetAsync("api/v1/Tenant");
|
var response = await _fixture.ServerClient.GetAsync("api/v1/Tenant");
|
||||||
|
|
||||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||||
|
|
||||||
var message = await response.Content.ReadAsJsonAsync<IEnumerable<TenantViewModel>>();
|
var message = await response.Content.ReadAsJsonAsync<IEnumerable<TenantViewModel>>();
|
||||||
|
|
||||||
message?.Data.Should().NotBeEmpty();
|
message?.Data.Should().NotBeEmpty();
|
||||||
message!.Data.Should().HaveCountGreaterOrEqualTo(2);
|
message!.Data.Should().HaveCountGreaterOrEqualTo(2);
|
||||||
message.Data!
|
message.Data!
|
||||||
.FirstOrDefault(x => x.Id == _fixture.CreatedTenantId)
|
.FirstOrDefault(x => x.Id == _fixture.CreatedTenantId)
|
||||||
.Should().NotBeNull();
|
.Should().NotBeNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
[Priority(10)]
|
[Priority(10)]
|
||||||
public async Task Should_Create_Tenant()
|
public async Task Should_Create_Tenant()
|
||||||
{
|
{
|
||||||
await _fixture.AuthenticateUserAsync();
|
await _fixture.AuthenticateUserAsync();
|
||||||
|
|
||||||
var request = new CreateTenantViewModel("Test Tenant 2");
|
var request = new CreateTenantViewModel("Test Tenant 2");
|
||||||
|
|
||||||
var response = await _fixture.ServerClient.PostAsJsonAsync("/api/v1/Tenant", request);
|
var response = await _fixture.ServerClient.PostAsJsonAsync("/api/v1/Tenant", request);
|
||||||
|
|
||||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||||
|
|
||||||
var message = await response.Content.ReadAsJsonAsync<Guid>();
|
var message = await response.Content.ReadAsJsonAsync<Guid>();
|
||||||
var tenantId = message?.Data;
|
var tenantId = message?.Data;
|
||||||
|
|
||||||
// Check if tenant exists
|
// Check if tenant exists
|
||||||
var tenantResponse = await _fixture.ServerClient.GetAsync($"/api/v1/Tenant/{tenantId}");
|
var tenantResponse = await _fixture.ServerClient.GetAsync($"/api/v1/Tenant/{tenantId}");
|
||||||
|
|
||||||
tenantResponse.StatusCode.Should().Be(HttpStatusCode.OK);
|
tenantResponse.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||||
|
|
||||||
var tenantMessage = await tenantResponse.Content.ReadAsJsonAsync<TenantViewModel>();
|
var tenantMessage = await tenantResponse.Content.ReadAsJsonAsync<TenantViewModel>();
|
||||||
|
|
||||||
tenantMessage?.Data.Should().NotBeNull();
|
tenantMessage?.Data.Should().NotBeNull();
|
||||||
|
|
||||||
tenantMessage!.Data!.Id.Should().Be(tenantId!.Value);
|
tenantMessage!.Data!.Id.Should().Be(tenantId!.Value);
|
||||||
tenantMessage.Data.Name.Should().Be(request.Name);
|
tenantMessage.Data.Name.Should().Be(request.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
[Priority(15)]
|
[Priority(15)]
|
||||||
public async Task Should_Update_Tenant()
|
public async Task Should_Update_Tenant()
|
||||||
{
|
{
|
||||||
await _fixture.AuthenticateUserAsync();
|
await _fixture.AuthenticateUserAsync();
|
||||||
|
|
||||||
var request = new UpdateTenantViewModel(_fixture.CreatedTenantId, "Test Tenant 3");
|
var request = new UpdateTenantViewModel(_fixture.CreatedTenantId, "Test Tenant 3");
|
||||||
|
|
||||||
var response = await _fixture.ServerClient.PutAsJsonAsync("/api/v1/Tenant", request);
|
var response = await _fixture.ServerClient.PutAsJsonAsync("/api/v1/Tenant", request);
|
||||||
|
|
||||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||||
|
|
||||||
var message = await response.Content.ReadAsJsonAsync<UpdateTenantViewModel>();
|
var message = await response.Content.ReadAsJsonAsync<UpdateTenantViewModel>();
|
||||||
|
|
||||||
message?.Data.Should().NotBeNull();
|
message?.Data.Should().NotBeNull();
|
||||||
message!.Data.Should().BeEquivalentTo(request);
|
message!.Data.Should().BeEquivalentTo(request);
|
||||||
|
|
||||||
// Check if tenant is updated
|
// Check if tenant is updated
|
||||||
var tenantResponse = await _fixture.ServerClient.GetAsync($"/api/v1/Tenant/{_fixture.CreatedTenantId}");
|
var tenantResponse = await _fixture.ServerClient.GetAsync($"/api/v1/Tenant/{_fixture.CreatedTenantId}");
|
||||||
|
|
||||||
tenantResponse.StatusCode.Should().Be(HttpStatusCode.OK);
|
tenantResponse.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||||
|
|
||||||
var tenantMessage = await response.Content.ReadAsJsonAsync<TenantViewModel>();
|
var tenantMessage = await response.Content.ReadAsJsonAsync<TenantViewModel>();
|
||||||
|
|
||||||
tenantMessage?.Data.Should().NotBeNull();
|
tenantMessage?.Data.Should().NotBeNull();
|
||||||
|
|
||||||
tenantMessage!.Data!.Id.Should().Be(_fixture.CreatedTenantId);
|
tenantMessage!.Data!.Id.Should().Be(_fixture.CreatedTenantId);
|
||||||
tenantMessage.Data.Name.Should().Be(request.Name);
|
tenantMessage.Data.Name.Should().Be(request.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
[Priority(20)]
|
[Priority(20)]
|
||||||
public async Task Should_Delete_Tenant()
|
public async Task Should_Delete_Tenant()
|
||||||
{
|
{
|
||||||
await _fixture.AuthenticateUserAsync();
|
await _fixture.AuthenticateUserAsync();
|
||||||
|
|
||||||
var response = await _fixture.ServerClient.DeleteAsync($"/api/v1/Tenant/{_fixture.CreatedTenantId}");
|
var response = await _fixture.ServerClient.DeleteAsync($"/api/v1/Tenant/{_fixture.CreatedTenantId}");
|
||||||
|
|
||||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||||
|
|
||||||
// Check if tenant is deleted
|
// Check if tenant is deleted
|
||||||
var tenantResponse = await _fixture.ServerClient.GetAsync($"/api/v1/Tenant/{_fixture.CreatedTenantId}");
|
var tenantResponse = await _fixture.ServerClient.GetAsync($"/api/v1/Tenant/{_fixture.CreatedTenantId}");
|
||||||
|
|
||||||
tenantResponse.StatusCode.Should().Be(HttpStatusCode.NotFound);
|
tenantResponse.StatusCode.Should().Be(HttpStatusCode.NotFound);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -26,13 +26,13 @@ public sealed class UserControllerTests : IClassFixture<UserTestFixture>
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Todo: Refactor tests to work alone
|
// Todo: Refactor tests to work alone
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
[Priority(0)]
|
[Priority(0)]
|
||||||
public async Task Should_Create_User()
|
public async Task Should_Create_User()
|
||||||
{
|
{
|
||||||
await _fixture.AuthenticateUserAsync();
|
await _fixture.AuthenticateUserAsync();
|
||||||
|
|
||||||
var user = new CreateUserViewModel(
|
var user = new CreateUserViewModel(
|
||||||
_fixture.CreatedUserEmail,
|
_fixture.CreatedUserEmail,
|
||||||
"Test",
|
"Test",
|
||||||
@ -237,7 +237,7 @@ public sealed class UserControllerTests : IClassFixture<UserTestFixture>
|
|||||||
|
|
||||||
var content = message!.Data;
|
var content = message!.Data;
|
||||||
content.Should().Be(_fixture.CreatedUserId);
|
content.Should().Be(_fixture.CreatedUserId);
|
||||||
|
|
||||||
// Todo: Check if stuff is done
|
// Todo: Check if stuff is done
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -7,7 +7,7 @@ namespace CleanArchitecture.IntegrationTests.Fixtures;
|
|||||||
public sealed class TenantTestFixture : TestFixtureBase
|
public sealed class TenantTestFixture : TestFixtureBase
|
||||||
{
|
{
|
||||||
public Guid CreatedTenantId { get; } = Guid.NewGuid();
|
public Guid CreatedTenantId { get; } = Guid.NewGuid();
|
||||||
|
|
||||||
protected override void SeedTestData(ApplicationDbContext context)
|
protected override void SeedTestData(ApplicationDbContext context)
|
||||||
{
|
{
|
||||||
context.Tenants.Add(new Tenant(
|
context.Tenants.Add(new Tenant(
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Net;
|
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using CleanArchitecture.Application.ViewModels.Users;
|
using CleanArchitecture.Application.ViewModels.Users;
|
||||||
@ -36,7 +35,7 @@ public class TestFixtureBase
|
|||||||
IServiceProvider scopedServices)
|
IServiceProvider scopedServices)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
// Todo: Fix auth
|
// Todo: Fix auth
|
||||||
public virtual async Task AuthenticateUserAsync()
|
public virtual async Task AuthenticateUserAsync()
|
||||||
{
|
{
|
||||||
|
@ -7,9 +7,6 @@ namespace CleanArchitecture.IntegrationTests.Fixtures.gRPC;
|
|||||||
|
|
||||||
public sealed class GetTenantsByIdsTestFixture : TestFixtureBase
|
public sealed class GetTenantsByIdsTestFixture : TestFixtureBase
|
||||||
{
|
{
|
||||||
public GrpcChannel GrpcChannel { get; }
|
|
||||||
public Guid CreatedTenantId { get; } = Guid.NewGuid();
|
|
||||||
|
|
||||||
public GetTenantsByIdsTestFixture()
|
public GetTenantsByIdsTestFixture()
|
||||||
{
|
{
|
||||||
GrpcChannel = GrpcChannel.ForAddress("http://localhost", new GrpcChannelOptions
|
GrpcChannel = GrpcChannel.ForAddress("http://localhost", new GrpcChannelOptions
|
||||||
@ -18,6 +15,9 @@ public sealed class GetTenantsByIdsTestFixture : TestFixtureBase
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public GrpcChannel GrpcChannel { get; }
|
||||||
|
public Guid CreatedTenantId { get; } = Guid.NewGuid();
|
||||||
|
|
||||||
protected override void SeedTestData(ApplicationDbContext context)
|
protected override void SeedTestData(ApplicationDbContext context)
|
||||||
{
|
{
|
||||||
base.SeedTestData(context);
|
base.SeedTestData(context);
|
||||||
@ -30,7 +30,7 @@ public sealed class GetTenantsByIdsTestFixture : TestFixtureBase
|
|||||||
|
|
||||||
public Tenant CreateTenant()
|
public Tenant CreateTenant()
|
||||||
{
|
{
|
||||||
return new(
|
return new Tenant(
|
||||||
CreatedTenantId,
|
CreatedTenantId,
|
||||||
"Test Tenant");
|
"Test Tenant");
|
||||||
}
|
}
|
||||||
|
@ -9,9 +9,6 @@ namespace CleanArchitecture.IntegrationTests.Fixtures.gRPC;
|
|||||||
|
|
||||||
public sealed class GetUsersByIdsTestFixture : TestFixtureBase
|
public sealed class GetUsersByIdsTestFixture : TestFixtureBase
|
||||||
{
|
{
|
||||||
public GrpcChannel GrpcChannel { get; }
|
|
||||||
public Guid CreatedUserId { get; } = Guid.NewGuid();
|
|
||||||
|
|
||||||
public GetUsersByIdsTestFixture()
|
public GetUsersByIdsTestFixture()
|
||||||
{
|
{
|
||||||
GrpcChannel = GrpcChannel.ForAddress("http://localhost", new GrpcChannelOptions
|
GrpcChannel = GrpcChannel.ForAddress("http://localhost", new GrpcChannelOptions
|
||||||
@ -20,6 +17,9 @@ public sealed class GetUsersByIdsTestFixture : TestFixtureBase
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public GrpcChannel GrpcChannel { get; }
|
||||||
|
public Guid CreatedUserId { get; } = Guid.NewGuid();
|
||||||
|
|
||||||
protected override void SeedTestData(ApplicationDbContext context)
|
protected override void SeedTestData(ApplicationDbContext context)
|
||||||
{
|
{
|
||||||
base.SeedTestData(context);
|
base.SeedTestData(context);
|
||||||
|
@ -47,19 +47,19 @@ public sealed class CleanArchitectureWebApplicationFactory : WebApplicationFacto
|
|||||||
services.SetupTestDatabase<EventStoreDbContext>(_connection);
|
services.SetupTestDatabase<EventStoreDbContext>(_connection);
|
||||||
services.SetupTestDatabase<DomainNotificationStoreDbContext>(_connection);
|
services.SetupTestDatabase<DomainNotificationStoreDbContext>(_connection);
|
||||||
|
|
||||||
ServiceProvider sp = services.BuildServiceProvider();
|
var sp = services.BuildServiceProvider();
|
||||||
|
|
||||||
using IServiceScope scope = sp.CreateScope();
|
using var scope = sp.CreateScope();
|
||||||
IServiceProvider scopedServices = scope.ServiceProvider;
|
var scopedServices = scope.ServiceProvider;
|
||||||
|
|
||||||
ApplicationDbContext applicationDbContext = scopedServices.GetRequiredService<ApplicationDbContext>();
|
var applicationDbContext = scopedServices.GetRequiredService<ApplicationDbContext>();
|
||||||
EventStoreDbContext storeDbContext = scopedServices.GetRequiredService<EventStoreDbContext>();
|
var storeDbContext = scopedServices.GetRequiredService<EventStoreDbContext>();
|
||||||
DomainNotificationStoreDbContext domainStoreDbContext = scopedServices.GetRequiredService<DomainNotificationStoreDbContext>();
|
var domainStoreDbContext = scopedServices.GetRequiredService<DomainNotificationStoreDbContext>();
|
||||||
|
|
||||||
applicationDbContext.EnsureMigrationsApplied();
|
applicationDbContext.EnsureMigrationsApplied();
|
||||||
|
|
||||||
var creator2 = (RelationalDatabaseCreator)storeDbContext.Database
|
var creator2 = (RelationalDatabaseCreator)storeDbContext.Database
|
||||||
.GetService<IRelationalDatabaseCreator>();
|
.GetService<IRelationalDatabaseCreator>();
|
||||||
creator2.CreateTables();
|
creator2.CreateTables();
|
||||||
|
|
||||||
var creator3 = (RelationalDatabaseCreator)domainStoreDbContext
|
var creator3 = (RelationalDatabaseCreator)domainStoreDbContext
|
||||||
|
@ -26,9 +26,9 @@ public sealed class GetTenantsByIdsTests : IClassFixture<GetTenantsByIdsTestFixt
|
|||||||
request.Ids.Add(_fixture.CreatedTenantId.ToString());
|
request.Ids.Add(_fixture.CreatedTenantId.ToString());
|
||||||
|
|
||||||
var response = await client.GetByIdsAsync(request);
|
var response = await client.GetByIdsAsync(request);
|
||||||
|
|
||||||
response.Tenants.Should().HaveCount(1);
|
response.Tenants.Should().HaveCount(1);
|
||||||
|
|
||||||
var tenant = response.Tenants.First();
|
var tenant = response.Tenants.First();
|
||||||
var createdTenant = _fixture.CreateTenant();
|
var createdTenant = _fixture.CreateTenant();
|
||||||
|
|
||||||
|
@ -6,16 +6,16 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Protobuf Include="Users\Models.proto" GrpcServices="Both" />
|
<Protobuf Include="Users\Models.proto" GrpcServices="Both"/>
|
||||||
<Protobuf Include="Users\UsersApi.proto" GrpcServices="Both" />
|
<Protobuf Include="Users\UsersApi.proto" GrpcServices="Both"/>
|
||||||
<Protobuf Include="Tenants\Models.proto" GrpcServices="Both" />
|
<Protobuf Include="Tenants\Models.proto" GrpcServices="Both"/>
|
||||||
<Protobuf Include="Tenants\TenantsApi.proto" GrpcServices="Both" />
|
<Protobuf Include="Tenants\TenantsApi.proto" GrpcServices="Both"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Google.Protobuf" Version="3.24.0" />
|
<PackageReference Include="Google.Protobuf" Version="3.24.0"/>
|
||||||
<PackageReference Include="Google.Protobuf.Tools" Version="3.24.0" />
|
<PackageReference Include="Google.Protobuf.Tools" Version="3.24.0"/>
|
||||||
<PackageReference Include="Grpc.AspNetCore" Version="2.55.0" />
|
<PackageReference Include="Grpc.AspNetCore" Version="2.55.0"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net7.0</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -7,4 +7,4 @@ public sealed record UserViewModel(
|
|||||||
string Email,
|
string Email,
|
||||||
string FirstName,
|
string FirstName,
|
||||||
string LastName,
|
string LastName,
|
||||||
bool IsDeleted);
|
bool IsDeleted);
|
@ -8,11 +8,11 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="FluentAssertions" Version="6.11.0" />
|
<PackageReference Include="FluentAssertions" Version="6.11.0"/>
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.0"/>
|
||||||
<PackageReference Include="MockQueryable.NSubstitute" Version="7.0.0" />
|
<PackageReference Include="MockQueryable.NSubstitute" Version="7.0.0"/>
|
||||||
<PackageReference Include="NSubstitute" Version="5.0.0" />
|
<PackageReference Include="NSubstitute" Version="5.0.0"/>
|
||||||
<PackageReference Include="xunit" Version="2.5.0" />
|
<PackageReference Include="xunit" Version="2.5.0"/>
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
@ -24,9 +24,9 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\CleanArchitecture.Application\CleanArchitecture.Application.csproj" />
|
<ProjectReference Include="..\CleanArchitecture.Application\CleanArchitecture.Application.csproj"/>
|
||||||
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj" />
|
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj"/>
|
||||||
<ProjectReference Include="..\CleanArchitecture.gRPC\CleanArchitecture.gRPC.csproj" />
|
<ProjectReference Include="..\CleanArchitecture.gRPC\CleanArchitecture.gRPC.csproj"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -10,24 +10,24 @@ namespace CleanArchitecture.gRPC.Tests.Fixtures;
|
|||||||
|
|
||||||
public sealed class TenantTestFixture
|
public sealed class TenantTestFixture
|
||||||
{
|
{
|
||||||
public TenantsApiImplementation TenantsApiImplementation { get; }
|
|
||||||
private ITenantRepository TenantRepository { get; }
|
|
||||||
|
|
||||||
public IEnumerable<Tenant> ExistingTenants { get; }
|
|
||||||
|
|
||||||
public TenantTestFixture()
|
public TenantTestFixture()
|
||||||
{
|
{
|
||||||
TenantRepository = Substitute.For<ITenantRepository>();
|
TenantRepository = Substitute.For<ITenantRepository>();
|
||||||
|
|
||||||
ExistingTenants = new List<Tenant>
|
ExistingTenants = new List<Tenant>
|
||||||
{
|
{
|
||||||
new Tenant(Guid.NewGuid(), "Tenant 1"),
|
new(Guid.NewGuid(), "Tenant 1"),
|
||||||
new Tenant(Guid.NewGuid(), "Tenant 2"),
|
new(Guid.NewGuid(), "Tenant 2"),
|
||||||
new Tenant(Guid.NewGuid(), "Tenant 3"),
|
new(Guid.NewGuid(), "Tenant 3")
|
||||||
};
|
};
|
||||||
|
|
||||||
TenantRepository.GetAllNoTracking().Returns(ExistingTenants.BuildMock());
|
TenantRepository.GetAllNoTracking().Returns(ExistingTenants.BuildMock());
|
||||||
|
|
||||||
TenantsApiImplementation = new(TenantRepository);
|
TenantsApiImplementation = new TenantsApiImplementation(TenantRepository);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TenantsApiImplementation TenantsApiImplementation { get; }
|
||||||
|
private ITenantRepository TenantRepository { get; }
|
||||||
|
|
||||||
|
public IEnumerable<Tenant> ExistingTenants { get; }
|
||||||
}
|
}
|
@ -24,7 +24,7 @@ public sealed class GetTenantsByIdsTests : IClassFixture<TenantTestFixture>
|
|||||||
var result = await _fixture.TenantsApiImplementation.GetByIds(
|
var result = await _fixture.TenantsApiImplementation.GetByIds(
|
||||||
SetupRequest(Enumerable.Empty<Guid>()),
|
SetupRequest(Enumerable.Empty<Guid>()),
|
||||||
default!);
|
default!);
|
||||||
|
|
||||||
result.Tenants.Should().HaveCount(0);
|
result.Tenants.Should().HaveCount(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,36 +37,36 @@ public sealed class GetTenantsByIdsTests : IClassFixture<TenantTestFixture>
|
|||||||
.Take(2)
|
.Take(2)
|
||||||
.Select(tenant => tenant.Id)
|
.Select(tenant => tenant.Id)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
ids.Add(nonExistingId);
|
ids.Add(nonExistingId);
|
||||||
|
|
||||||
var result = await _fixture.TenantsApiImplementation.GetByIds(
|
var result = await _fixture.TenantsApiImplementation.GetByIds(
|
||||||
SetupRequest(ids),
|
SetupRequest(ids),
|
||||||
default!);
|
default!);
|
||||||
|
|
||||||
result.Tenants.Should().HaveCount(2);
|
result.Tenants.Should().HaveCount(2);
|
||||||
|
|
||||||
foreach (var tenant in result.Tenants)
|
foreach (var tenant in result.Tenants)
|
||||||
{
|
{
|
||||||
var tenantId = Guid.Parse(tenant.Id);
|
var tenantId = Guid.Parse(tenant.Id);
|
||||||
|
|
||||||
tenantId.Should().NotBe(nonExistingId);
|
tenantId.Should().NotBe(nonExistingId);
|
||||||
|
|
||||||
var mockTenant = _fixture.ExistingTenants.First(t => t.Id == tenantId);
|
var mockTenant = _fixture.ExistingTenants.First(t => t.Id == tenantId);
|
||||||
|
|
||||||
mockTenant.Should().NotBeNull();
|
mockTenant.Should().NotBeNull();
|
||||||
|
|
||||||
tenant.Name.Should().Be(mockTenant.Name);
|
tenant.Name.Should().Be(mockTenant.Name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static GetTenantsByIdsRequest SetupRequest(IEnumerable<Guid> ids)
|
private static GetTenantsByIdsRequest SetupRequest(IEnumerable<Guid> ids)
|
||||||
{
|
{
|
||||||
var request = new GetTenantsByIdsRequest();
|
var request = new GetTenantsByIdsRequest();
|
||||||
|
|
||||||
request.Ids.AddRange(ids.Select(id => id.ToString()));
|
request.Ids.AddRange(ids.Select(id => id.ToString()));
|
||||||
request.Ids.Add("Not a guid");
|
request.Ids.Add("Not a guid");
|
||||||
|
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,18 +4,15 @@ namespace CleanArchitecture.gRPC;
|
|||||||
|
|
||||||
public sealed class CleanArchitecture : ICleanArchitecture
|
public sealed class CleanArchitecture : ICleanArchitecture
|
||||||
{
|
{
|
||||||
private readonly IUsersContext _users;
|
|
||||||
private readonly ITenantsContext _tenants;
|
|
||||||
|
|
||||||
public IUsersContext Users => _users;
|
|
||||||
public ITenantsContext Tenants => _tenants;
|
|
||||||
|
|
||||||
public CleanArchitecture(
|
public CleanArchitecture(
|
||||||
IUsersContext users,
|
IUsersContext users,
|
||||||
ITenantsContext tenants)
|
ITenantsContext tenants)
|
||||||
{
|
{
|
||||||
_users = users;
|
Users = users;
|
||||||
_tenants = tenants;
|
Tenants = tenants;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public IUsersContext Users { get; }
|
||||||
|
|
||||||
|
public ITenantsContext Tenants { get; }
|
||||||
|
}
|
@ -6,9 +6,9 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj" />
|
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj"/>
|
||||||
<ProjectReference Include="..\CleanArchitecture.Proto\CleanArchitecture.Proto.csproj" />
|
<ProjectReference Include="..\CleanArchitecture.Proto\CleanArchitecture.Proto.csproj"/>
|
||||||
<ProjectReference Include="..\CleanArchitecture.Shared\CleanArchitecture.Shared.csproj" />
|
<ProjectReference Include="..\CleanArchitecture.Shared\CleanArchitecture.Shared.csproj"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -20,11 +20,11 @@ public sealed class TenantsContext : ITenantsContext
|
|||||||
public async Task<IEnumerable<TenantViewModel>> GetTenantsByIds(IEnumerable<Guid> ids)
|
public async Task<IEnumerable<TenantViewModel>> GetTenantsByIds(IEnumerable<Guid> ids)
|
||||||
{
|
{
|
||||||
var request = new GetTenantsByIdsRequest();
|
var request = new GetTenantsByIdsRequest();
|
||||||
|
|
||||||
request.Ids.AddRange(ids.Select(id => id.ToString()));
|
request.Ids.AddRange(ids.Select(id => id.ToString()));
|
||||||
|
|
||||||
var result = await _client.GetByIdsAsync(request);
|
var result = await _client.GetByIdsAsync(request);
|
||||||
|
|
||||||
return result.Tenants.Select(tenant => new TenantViewModel(
|
return result.Tenants.Select(tenant => new TenantViewModel(
|
||||||
Guid.Parse(tenant.Id),
|
Guid.Parse(tenant.Id),
|
||||||
tenant.Name));
|
tenant.Name));
|
||||||
|
@ -32,4 +32,4 @@ public sealed class UsersContext : IUsersContext
|
|||||||
user.LastName,
|
user.LastName,
|
||||||
user.IsDeleted));
|
user.IsDeleted));
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -53,7 +53,7 @@ public static class ServiceCollectionExtensions
|
|||||||
|
|
||||||
services.AddSingleton<IUsersContext, UsersContext>();
|
services.AddSingleton<IUsersContext, UsersContext>();
|
||||||
services.AddSingleton<ITenantsContext, TenantsContext>();
|
services.AddSingleton<ITenantsContext, TenantsContext>();
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,4 +6,4 @@ public interface ICleanArchitecture
|
|||||||
{
|
{
|
||||||
IUsersContext Users { get; }
|
IUsersContext Users { get; }
|
||||||
ITenantsContext Tenants { get; }
|
ITenantsContext Tenants { get; }
|
||||||
}
|
}
|
@ -8,4 +8,4 @@ namespace CleanArchitecture.gRPC.Interfaces;
|
|||||||
public interface IUsersContext
|
public interface IUsersContext
|
||||||
{
|
{
|
||||||
Task<IEnumerable<UserViewModel>> GetUsersByIds(IEnumerable<Guid> ids);
|
Task<IEnumerable<UserViewModel>> GetUsersByIds(IEnumerable<Guid> ids);
|
||||||
}
|
}
|
@ -3,4 +3,4 @@
|
|||||||
public sealed class GRPCSettings
|
public sealed class GRPCSettings
|
||||||
{
|
{
|
||||||
public string CleanArchitectureUrl { get; set; } = string.Empty;
|
public string CleanArchitectureUrl { get; set; } = string.Empty;
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user