Skip to content

Commit 4eedce6

Browse files
authored
Merge pull request #13 from sj-distributor/tweak
Enrich infrastructure
2 parents 7046f5b + 7ca39e9 commit 4eedce6

29 files changed

+483
-318
lines changed

src/Wax.Api/Controllers/CustomerController.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
using Mediator.Net;
22
using Microsoft.AspNetCore.Mvc;
33
using Wax.Messages.Commands.Customers;
4+
using Wax.Messages.Dtos.Customers;
5+
using Wax.Messages.Requests;
46
using Wax.Messages.Requests.Customers;
57

68
namespace Wax.Api.Controllers
@@ -16,12 +18,11 @@ public CustomerController(IMediator mediator)
1618
_mediator = mediator;
1719
}
1820

19-
[HttpGet("{id:guid}")]
20-
[ProducesResponseType(typeof(GetCustomerResponse), 200)]
21-
public async Task<IActionResult> GetAsync(Guid id)
21+
[HttpGet]
22+
[ProducesResponseType(typeof(PaginatedResponse<CustomerShortInfo>), 200)]
23+
public async Task<IActionResult> GetListAsync([FromQuery] GetCustomersRequest request)
2224
{
23-
var response = await _mediator.RequestAsync<GetCustomerRequest, GetCustomerResponse>(
24-
new GetCustomerRequest { CustomerId = id });
25+
var response = await _mediator.RequestAsync<GetCustomersRequest, PaginatedResponse<CustomerShortInfo>>(request);
2526

2627
return Ok(response);
2728
}

src/Wax.Api/Filters/GlobalExceptionFilter.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ private void HandleBusinessException(ExceptionContext context)
4242

4343
var problemDetails = new ProblemDetails
4444
{
45+
Type = context.Exception.GetType().Name,
4546
Status = StatusCodes.Status409Conflict,
4647
Title = "A business error occur.",
4748
Detail = context.Exception.Message,
@@ -59,6 +60,7 @@ private void HandleEntityNotFoundException(ExceptionContext context)
5960

6061
var details = new ProblemDetails
6162
{
63+
Type = nameof(EntityNotFoundException),
6264
Status = StatusCodes.Status404NotFound,
6365
Title = "The specified resource was not found.",
6466
Detail = exception.Message,
@@ -72,7 +74,10 @@ private void HandleValidationException(ExceptionContext context)
7274
var exception = context.Exception as FluentValidation.ValidationException;
7375

7476
var details = new ValidationProblemDetails(exception.Errors.GroupBy(e => e.PropertyName, e => e.ErrorMessage)
75-
.ToDictionary(failureGroup => failureGroup.Key, failureGroup => failureGroup.ToArray()));
77+
.ToDictionary(failureGroup => failureGroup.Key, failureGroup => failureGroup.ToArray()))
78+
{
79+
Type = nameof(FluentValidation.ValidationException)
80+
};
7681

7782
context.Result = new BadRequestObjectResult(details);
7883

@@ -85,6 +90,7 @@ private void HandleInternalServerError(ExceptionContext context)
8590

8691
var problemDetails = new ProblemDetails
8792
{
93+
Type = context.Exception.GetType().Name,
8894
Status = StatusCodes.Status500InternalServerError,
8995
Title = "Internal error.",
9096
Detail = _env.IsDevelopment() ? context.Exception.Message : "An error occur. Try it again later."

src/Wax.Core/Data/ApplicationDbContext.cs

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : ba
99
{
1010
}
1111

12-
public bool HasEntitiesChanged { get; private set; }
13-
1412
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
1513
{
1614
optionsBuilder.UseInMemoryDatabase("__wax_database");
@@ -20,24 +18,4 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
2018
{
2119
modelBuilder.ApplyConfigurationsFromAssembly(typeof(CustomerEntityTypeConfiguration).Assembly);
2220
}
23-
24-
25-
public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken())
26-
{
27-
var result = base.SaveChangesAsync(cancellationToken);
28-
29-
HasEntitiesChanged = false;
30-
31-
return result;
32-
}
33-
34-
public async Task ChangeEntitiesAsync(bool saveNow = false, CancellationToken cancellationToken = default)
35-
{
36-
HasEntitiesChanged = true;
37-
38-
if (saveNow)
39-
{
40-
await SaveChangesAsync(cancellationToken);
41-
}
42-
}
4321
}

src/Wax.Core/Data/IUnitOfWork.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,15 @@ public UnitOfWork(ApplicationDbContext context)
1616

1717
public Task CommitAsync(CancellationToken cancellationToken = default)
1818
{
19-
return _context.HasEntitiesChanged ? _context.SaveChangesAsync(cancellationToken) : Task.CompletedTask;
19+
return _context.SaveChangesAsync(cancellationToken);
20+
}
21+
}
22+
23+
public static class UnitOfWorkExtensions
24+
{
25+
public static async Task WithUnitOfWork(this IUnitOfWork uow, Func<Task> func)
26+
{
27+
await func();
28+
await uow.CommitAsync();
2029
}
2130
}
Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,50 @@
11
using AutoMapper;
2+
using FluentValidation;
23
using Mediator.Net.Context;
34
using Mediator.Net.Contracts;
4-
using Wax.Core.Data;
55
using Wax.Core.Domain.Customers;
66
using Wax.Core.Domain.Customers.Exceptions;
7+
using Wax.Core.Middlewares.FluentMessageValidator;
78
using Wax.Core.Repositories;
89
using Wax.Messages.Commands.Customers;
910

10-
namespace Wax.Core.Handlers.CommandHandlers.Customers
11+
namespace Wax.Core.Handlers.CommandHandlers.Customers;
12+
13+
public class CreateCustomerCommandHandler : ICommandHandler<CreateCustomerCommand, CreateCustomerResponse>
1114
{
12-
public class CreateCustomerCommandHandler : ICommandHandler<CreateCustomerCommand, CreateCustomerResponse>
15+
private readonly IMapper _mapper;
16+
private readonly ICustomerRepository _customerRepository;
17+
18+
public CreateCustomerCommandHandler(IMapper mapper, ICustomerRepository customerRepository)
1319
{
14-
private readonly IMapper _mapper;
15-
private readonly ICustomerRepository _customerRepository;
20+
_mapper = mapper;
21+
_customerRepository = customerRepository;
22+
}
1623

17-
public CreateCustomerCommandHandler(IMapper mapper,ICustomerRepository customerRepository)
18-
{
19-
_mapper = mapper;
20-
_customerRepository = customerRepository;
21-
}
24+
public async Task<CreateCustomerResponse> Handle(IReceiveContext<CreateCustomerCommand> context,
25+
CancellationToken cancellationToken)
26+
{
27+
var isUnique = await _customerRepository.IsUniqueAsync(context.Message.Name);
2228

23-
public async Task<CreateCustomerResponse> Handle(IReceiveContext<CreateCustomerCommand> context,
24-
CancellationToken cancellationToken)
29+
if (!isUnique)
2530
{
26-
var existing = await _customerRepository.FindByNameAsync(context.Message.Name);
31+
throw new CustomerNameAlreadyExistsException();
32+
}
2733

28-
if (existing != null)
29-
{
30-
throw new CustomerNameAlreadyExistsException();
31-
}
34+
var customer = _mapper.Map<Customer>(context.Message);
3235

33-
var customer = _mapper.Map<Customer>(context.Message);
36+
await _customerRepository.InsertAsync(customer, cancellationToken);
3437

35-
await _customerRepository.InsertAsync(customer);
38+
return new CreateCustomerResponse { CustomerId = customer.Id };
39+
}
40+
}
3641

37-
return new CreateCustomerResponse { CustomerId = customer.Id };
38-
}
42+
public class CreateCustomerCommandValidator : FluentMessageValidator<CreateCustomerCommand>
43+
{
44+
public CreateCustomerCommandValidator()
45+
{
46+
RuleFor(v => v.Name).NotEmpty().MaximumLength(64);
47+
RuleFor(v => v.Address).MaximumLength(512);
48+
RuleFor(v => v.Contact).MaximumLength(128);
3949
}
4050
}

src/Wax.Core/Handlers/CommandHandlers/Customers/DeleteCustomerCommandHandler.cs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1+
using FluentValidation;
12
using Mediator.Net.Context;
23
using Mediator.Net.Contracts;
3-
using Wax.Core.Data;
4+
using Wax.Core.Middlewares.FluentMessageValidator;
45
using Wax.Core.Repositories;
56
using Wax.Messages.Commands.Customers;
67

@@ -17,8 +18,16 @@ public DeleteCustomerCommandHandler(ICustomerRepository customerRepository)
1718

1819
public async Task Handle(IReceiveContext<DeleteCustomerCommand> context, CancellationToken cancellationToken)
1920
{
20-
var customer = await _customerRepository.GetByIdAsync(context.Message.CustomerId);
21+
var customer = await _customerRepository.GetByIdAsync(context.Message.CustomerId, cancellationToken);
2122

22-
await _customerRepository.DeleteAsync(customer);
23+
await _customerRepository.DeleteAsync(customer, cancellationToken);
24+
}
25+
}
26+
27+
public class DeleteCustomerCommandValidator : FluentMessageValidator<DeleteCustomerCommand>
28+
{
29+
public DeleteCustomerCommandValidator()
30+
{
31+
RuleFor(v => v.CustomerId).NotEmpty();
2332
}
2433
}

src/Wax.Core/Handlers/CommandHandlers/Customers/UpdateCustomerCommandHandler.cs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
using AutoMapper;
2+
using FluentValidation;
23
using Mediator.Net.Context;
34
using Mediator.Net.Contracts;
45
using Wax.Core.Domain.Customers.Exceptions;
6+
using Wax.Core.Middlewares.FluentMessageValidator;
57
using Wax.Core.Repositories;
68
using Wax.Messages.Commands.Customers;
79

@@ -20,20 +22,31 @@ public UpdateCustomerCommandHandler(IMapper mapper, ICustomerRepository customer
2022

2123
public async Task Handle(IReceiveContext<UpdateCustomerCommand> context, CancellationToken cancellationToken)
2224
{
23-
var customer = await _customerRepository.GetByIdAsync(context.Message.CustomerId);
25+
var customer = await _customerRepository.GetByIdAsync(context.Message.CustomerId, cancellationToken);
2426

2527
if (customer.Name != context.Message.Name)
2628
{
27-
var existing = await _customerRepository.FindByNameAsync(context.Message.Name);
29+
var isUnique = await _customerRepository.IsUniqueAsync(context.Message.Name);
2830

29-
if (existing != null)
31+
if (!isUnique)
3032
{
3133
throw new CustomerNameAlreadyExistsException();
3234
}
3335
}
3436

3537
_mapper.Map(context.Message, customer);
3638

37-
await _customerRepository.UpdateAsync(customer);
39+
await _customerRepository.UpdateAsync(customer, cancellationToken);
40+
}
41+
}
42+
43+
public class UpdateCustomerCommandValidator : FluentMessageValidator<UpdateCustomerCommand>
44+
{
45+
public UpdateCustomerCommandValidator()
46+
{
47+
RuleFor(v => v.CustomerId).NotEmpty();
48+
RuleFor(v => v.Name).NotEmpty().MaximumLength(64);
49+
RuleFor(v => v.Address).MaximumLength(512);
50+
RuleFor(v => v.Contact).MaximumLength(128);
3851
}
3952
}

src/Wax.Core/Handlers/RequestHandlers/Customers/GetCustomerRequestHandler.cs

Lines changed: 0 additions & 32 deletions
This file was deleted.
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
using FluentValidation;
2+
using Mediator.Net.Context;
3+
using Mediator.Net.Contracts;
4+
using Wax.Core.Middlewares.FluentMessageValidator;
5+
using Wax.Core.Repositories;
6+
using Wax.Messages.Dtos.Customers;
7+
using Wax.Messages.Requests;
8+
using Wax.Messages.Requests.Customers;
9+
10+
namespace Wax.Core.Handlers.RequestHandlers.Customers;
11+
12+
public class GetCustomersRequestHandler : IRequestHandler<GetCustomersRequest, PaginatedResponse<CustomerShortInfo>>
13+
{
14+
private readonly ICustomerRepository _customerRepository;
15+
16+
public GetCustomersRequestHandler(ICustomerRepository customerRepository)
17+
{
18+
_customerRepository = customerRepository;
19+
}
20+
21+
public async Task<PaginatedResponse<CustomerShortInfo>> Handle(IReceiveContext<GetCustomersRequest> context,
22+
CancellationToken cancellationToken)
23+
{
24+
var data = await _customerRepository.GetPaginatedListByProjectionAsync(
25+
c => new CustomerShortInfo
26+
{
27+
Id = c.Id,
28+
Address = c.Address,
29+
Name = c.Name
30+
},
31+
orderBy: o => o.Name,
32+
pageIndex: context.Message.PageIndex,
33+
pageSize: context.Message.PageSize,
34+
cancellationToken: cancellationToken);
35+
36+
return new PaginatedResponse<CustomerShortInfo>(data);
37+
}
38+
}
39+
40+
public class GetCustomersRequestValidator : FluentMessageValidator<GetCustomersRequest>
41+
{
42+
public GetCustomersRequestValidator()
43+
{
44+
RuleFor(v => v.PageIndex).GreaterThan(0);
45+
RuleFor(v => v.PageSize).GreaterThan(0);
46+
}
47+
}

0 commit comments

Comments
 (0)