Skip to content

Commit

Permalink
Enrich infrastructure
Browse files Browse the repository at this point in the history
  • Loading branch information
linwenda committed Oct 31, 2023
1 parent 7046f5b commit 22689f4
Show file tree
Hide file tree
Showing 28 changed files with 481 additions and 301 deletions.
11 changes: 6 additions & 5 deletions src/Wax.Api/Controllers/CustomerController.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using Mediator.Net;
using Microsoft.AspNetCore.Mvc;
using Wax.Messages.Commands.Customers;
using Wax.Messages.Dtos.Customers;
using Wax.Messages.Requests;
using Wax.Messages.Requests.Customers;

namespace Wax.Api.Controllers
Expand All @@ -16,12 +18,11 @@ public CustomerController(IMediator mediator)
_mediator = mediator;
}

[HttpGet("{id:guid}")]
[ProducesResponseType(typeof(GetCustomerResponse), 200)]
public async Task<IActionResult> GetAsync(Guid id)
[HttpGet]
[ProducesResponseType(typeof(PaginatedResponse<CustomerShortInfo>), 200)]
public async Task<IActionResult> GetListAsync([FromQuery] GetCustomersRequest request)
{
var response = await _mediator.RequestAsync<GetCustomerRequest, GetCustomerResponse>(
new GetCustomerRequest { CustomerId = id });
var response = await _mediator.RequestAsync<GetCustomersRequest, PaginatedResponse<CustomerShortInfo>>(request);

return Ok(response);
}
Expand Down
8 changes: 7 additions & 1 deletion src/Wax.Api/Filters/GlobalExceptionFilter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ private void HandleBusinessException(ExceptionContext context)

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

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

var details = new ValidationProblemDetails(exception.Errors.GroupBy(e => e.PropertyName, e => e.ErrorMessage)
.ToDictionary(failureGroup => failureGroup.Key, failureGroup => failureGroup.ToArray()));
.ToDictionary(failureGroup => failureGroup.Key, failureGroup => failureGroup.ToArray()))
{
Type = nameof(FluentValidation.ValidationException)
};

context.Result = new BadRequestObjectResult(details);

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

var problemDetails = new ProblemDetails
{
Type = context.Exception.GetType().Name,
Status = StatusCodes.Status500InternalServerError,
Title = "Internal error.",
Detail = _env.IsDevelopment() ? context.Exception.Message : "An error occur. Try it again later."
Expand Down
22 changes: 0 additions & 22 deletions src/Wax.Core/Data/ApplicationDbContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : ba
{
}

public bool HasEntitiesChanged { get; private set; }

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseInMemoryDatabase("__wax_database");
Expand All @@ -20,24 +18,4 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfigurationsFromAssembly(typeof(CustomerEntityTypeConfiguration).Assembly);
}


public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken())
{
var result = base.SaveChangesAsync(cancellationToken);

HasEntitiesChanged = false;

return result;
}

public async Task ChangeEntitiesAsync(bool saveNow = false, CancellationToken cancellationToken = default)
{
HasEntitiesChanged = true;

if (saveNow)
{
await SaveChangesAsync(cancellationToken);
}
}
}
11 changes: 10 additions & 1 deletion src/Wax.Core/Data/IUnitOfWork.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@ public UnitOfWork(ApplicationDbContext context)

public Task CommitAsync(CancellationToken cancellationToken = default)
{
return _context.HasEntitiesChanged ? _context.SaveChangesAsync(cancellationToken) : Task.CompletedTask;
return _context.SaveChangesAsync(cancellationToken);
}
}

public static class UnitOfWorkExtensions
{
public static async Task WithUnitOfWork(this IUnitOfWork uow, Func<Task> func)
{
await func();
await uow.CommitAsync();
}
}
Original file line number Diff line number Diff line change
@@ -1,40 +1,50 @@
using AutoMapper;
using FluentValidation;
using Mediator.Net.Context;
using Mediator.Net.Contracts;
using Wax.Core.Data;
using Wax.Core.Domain.Customers;
using Wax.Core.Domain.Customers.Exceptions;
using Wax.Core.Middlewares.FluentMessageValidator;
using Wax.Core.Repositories;
using Wax.Messages.Commands.Customers;

namespace Wax.Core.Handlers.CommandHandlers.Customers
namespace Wax.Core.Handlers.CommandHandlers.Customers;

public class CreateCustomerCommandHandler : ICommandHandler<CreateCustomerCommand, CreateCustomerResponse>
{
public class CreateCustomerCommandHandler : ICommandHandler<CreateCustomerCommand, CreateCustomerResponse>
private readonly IMapper _mapper;
private readonly ICustomerRepository _customerRepository;

public CreateCustomerCommandHandler(IMapper mapper, ICustomerRepository customerRepository)
{
private readonly IMapper _mapper;
private readonly ICustomerRepository _customerRepository;
_mapper = mapper;
_customerRepository = customerRepository;
}

public CreateCustomerCommandHandler(IMapper mapper,ICustomerRepository customerRepository)
{
_mapper = mapper;
_customerRepository = customerRepository;
}
public async Task<CreateCustomerResponse> Handle(IReceiveContext<CreateCustomerCommand> context,
CancellationToken cancellationToken)
{
var isUnique = await _customerRepository.IsUniqueAsync(context.Message.Name);

public async Task<CreateCustomerResponse> Handle(IReceiveContext<CreateCustomerCommand> context,
CancellationToken cancellationToken)
if (!isUnique)
{
var existing = await _customerRepository.FindByNameAsync(context.Message.Name);
throw new CustomerNameAlreadyExistsException();
}

if (existing != null)
{
throw new CustomerNameAlreadyExistsException();
}
var customer = _mapper.Map<Customer>(context.Message);

var customer = _mapper.Map<Customer>(context.Message);
await _customerRepository.InsertAsync(customer, cancellationToken);

await _customerRepository.InsertAsync(customer);
return new CreateCustomerResponse { CustomerId = customer.Id };
}
}

return new CreateCustomerResponse { CustomerId = customer.Id };
}
public class CreateCustomerCommandValidator : FluentMessageValidator<CreateCustomerCommand>
{
public CreateCustomerCommandValidator()
{
RuleFor(v => v.Name).NotEmpty().MaximumLength(64);
RuleFor(v => v.Address).MaximumLength(512);
RuleFor(v => v.Contact).MaximumLength(128);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using FluentValidation;
using Mediator.Net.Context;
using Mediator.Net.Contracts;
using Wax.Core.Data;
using Wax.Core.Middlewares.FluentMessageValidator;
using Wax.Core.Repositories;
using Wax.Messages.Commands.Customers;

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

public async Task Handle(IReceiveContext<DeleteCustomerCommand> context, CancellationToken cancellationToken)
{
var customer = await _customerRepository.GetByIdAsync(context.Message.CustomerId);
var customer = await _customerRepository.GetByIdAsync(context.Message.CustomerId, cancellationToken);

await _customerRepository.DeleteAsync(customer);
await _customerRepository.DeleteAsync(customer, cancellationToken);
}
}

public class DeleteCustomerCommandValidator : FluentMessageValidator<DeleteCustomerCommand>
{
public DeleteCustomerCommandValidator()
{
RuleFor(v => v.CustomerId).NotEmpty();
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using AutoMapper;
using FluentValidation;
using Mediator.Net.Context;
using Mediator.Net.Contracts;
using Wax.Core.Domain.Customers.Exceptions;
using Wax.Core.Middlewares.FluentMessageValidator;
using Wax.Core.Repositories;
using Wax.Messages.Commands.Customers;

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

public async Task Handle(IReceiveContext<UpdateCustomerCommand> context, CancellationToken cancellationToken)
{
var customer = await _customerRepository.GetByIdAsync(context.Message.CustomerId);
var customer = await _customerRepository.GetByIdAsync(context.Message.CustomerId, cancellationToken);

if (customer.Name != context.Message.Name)
{
var existing = await _customerRepository.FindByNameAsync(context.Message.Name);
var isUnique = await _customerRepository.IsUniqueAsync(context.Message.Name);

if (existing != null)
if (!isUnique)
{
throw new CustomerNameAlreadyExistsException();
}
}

_mapper.Map(context.Message, customer);

await _customerRepository.UpdateAsync(customer);
await _customerRepository.UpdateAsync(customer, cancellationToken);
}
}

public class UpdateCustomerCommandValidator : FluentMessageValidator<UpdateCustomerCommand>
{
public UpdateCustomerCommandValidator()
{
RuleFor(v => v.CustomerId).NotEmpty();
RuleFor(v => v.Name).NotEmpty().MaximumLength(64);
RuleFor(v => v.Address).MaximumLength(512);
RuleFor(v => v.Contact).MaximumLength(128);
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using FluentValidation;
using Mediator.Net.Context;
using Mediator.Net.Contracts;
using Wax.Core.Middlewares.FluentMessageValidator;
using Wax.Core.Repositories;
using Wax.Messages.Dtos.Customers;
using Wax.Messages.Requests;
using Wax.Messages.Requests.Customers;

namespace Wax.Core.Handlers.RequestHandlers.Customers;

public class GetCustomersRequestHandler : IRequestHandler<GetCustomersRequest, PaginatedResponse<CustomerShortInfo>>
{
private readonly ICustomerRepository _customerRepository;

public GetCustomersRequestHandler(ICustomerRepository customerRepository)
{
_customerRepository = customerRepository;
}

public async Task<PaginatedResponse<CustomerShortInfo>> Handle(IReceiveContext<GetCustomersRequest> context,
CancellationToken cancellationToken)
{
var data = await _customerRepository.GetPaginatedListByProjectionAsync(
c => new CustomerShortInfo
{
Id = c.Id,
Address = c.Address,
Name = c.Name
},
orderBy: o => o.Name,
pageIndex: context.Message.PageIndex,
pageSize: context.Message.PageSize,
cancellationToken: cancellationToken);

return new PaginatedResponse<CustomerShortInfo>(data);
}
}

public class GetCustomersRequestValidator : FluentMessageValidator<GetCustomersRequest>
{
public GetCustomersRequestValidator()
{
RuleFor(v => v.PageIndex).GreaterThan(0);
RuleFor(v => v.PageSize).GreaterThan(0);
}
}
Loading

0 comments on commit 22689f4

Please sign in to comment.