Skip to content

Commit a2d402a

Browse files
authored
Release v0.6.4
* Adds BaseController.ErrorResponse * Adds Base error responses to BaseController * Adds Base SaveResponse responses to BaseController * Adds BalancesValidator, AccountValidator, TransactionValidator and CategoryValidator * Adds integration tests to cover validation errors * Adds Service unit tests to cover validation errors * Moves validations to Validator classes * Moves validators to Application layer * Changes Provider.GetById validations to Validator.ExistsAsync * Removes auto fluent validator * Removes IErrorMessageProvider from the service classes and moves to validator
1 parent 8345de9 commit a2d402a

55 files changed

Lines changed: 1819 additions & 648 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/api/common/FinancialHub.Common/Responses/Errors/ValidationsErrorResponse.cs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,23 @@
22
{
33
public class ValidationsErrorResponse : BaseErrorResponse
44
{
5-
public Dictionary<string, string[]>? Errors { get; set; }
5+
public FieldValidationErrorResponse[] Errors { get; protected set; }
66

7-
public ValidationsErrorResponse(string message, Dictionary<string, string[]>? errors = null)
8-
: base(400, message)
9-
{
7+
public ValidationsErrorResponse(string message, FieldValidationErrorResponse[] errors) : base(400, message)
8+
{
109
this.Errors = errors;
1110
}
11+
12+
public class FieldValidationErrorResponse
13+
{
14+
public string Field { get; protected set; }
15+
public string[] Messages { get; protected set; }
16+
17+
public FieldValidationErrorResponse(string field, string[] messages)
18+
{
19+
this.Field = field;
20+
this.Messages = messages;
21+
}
22+
}
1223
}
1324
}

src/api/common/FinancialHub.Common/Results/Errors/ServiceError.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
namespace FinancialHub.Common.Results.Errors
44
{
5-
public class ServiceError
5+
public abstract class ServiceError
66
{
77
#warning it can be changed to an enum or use a different value from https codes
88
public int Code { get; protected set; }
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
namespace FinancialHub.Common.Results.Errors
2+
{
3+
public class ValidationError : ServiceError
4+
{
5+
public FieldValidationError[] Errors { get; protected set; }
6+
7+
public ValidationError(string message, FieldValidationError[] errors) : base(400, message)
8+
{
9+
this.Errors = errors;
10+
}
11+
12+
public class FieldValidationError
13+
{
14+
public string Field { get; }
15+
public string[] Messages { get; }
16+
17+
public FieldValidationError(string field, string[] messages)
18+
{
19+
this.Field = field;
20+
this.Messages = messages;
21+
}
22+
}
23+
}
24+
}

src/api/common/FinancialHub.Common/Results/ServiceResult.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ public ServiceResult(ServiceError? error = null)
3535
this.Error = error;
3636
}
3737

38+
public static ServiceResult Success => new();
39+
3840
public static implicit operator ServiceResult(ServiceError? error)
3941
{
4042
return new ServiceResult(error: error);

src/api/core/FinancialHub.Core.Application/Extensions/Configurations/IServiceCollectionExtensions.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@
1010
using FinancialHub.Core.Application.Validators.Balances;
1111
using FinancialHub.Core.Application.Validators.Categories;
1212
using FinancialHub.Core.Application.Validators.Transactions;
13+
using FinancialHub.Core.Domain.Interfaces.Validators;
1314

1415
namespace FinancialHub.Core.Application.Extensions.Configurations
1516
{
16-
public static class IServiceCollectionExtensions
17+
public static partial class IServiceCollectionExtensions
1718
{
1819
public static IServiceCollection AddCoreServices(this IServiceCollection services)
1920
{
@@ -32,6 +33,8 @@ private static IServiceCollection AddBalanceService(this IServiceCollection serv
3233

3334
services.AddAutoMapper(typeof(BalanceMapper));
3435

36+
services.AddScoped<IBalancesValidator, BalancesValidator>();
37+
3538
services.AddScoped<IValidator<CreateBalanceDto>, CreateBalanceValidator>();
3639
services.AddScoped<IValidator<UpdateBalanceDto>, UpdateBalanceValidator>();
3740

@@ -44,6 +47,7 @@ private static IServiceCollection AddTransactionService(this IServiceCollection
4447

4548
services.AddAutoMapper(typeof(TransactionMapper));
4649

50+
services.AddScoped<ITransactionsValidator, TransactionsValidator>();
4751
services.AddScoped<IValidator<CreateTransactionDto>, CreateTransactionValidator>();
4852
services.AddScoped<IValidator<UpdateTransactionDto>, UpdateTransactionValidator>();
4953

@@ -56,6 +60,7 @@ private static IServiceCollection AddAccountService(this IServiceCollection serv
5660

5761
services.AddAutoMapper(typeof(AccountMapper));
5862

63+
services.AddScoped<IAccountsValidator, AccountsValidator>();
5964
services.AddScoped<IValidator<CreateAccountDto>, CreateAccountValidator>();
6065
services.AddScoped<IValidator<UpdateAccountDto>, UpdateAccountValidator>();
6166

@@ -67,7 +72,8 @@ private static IServiceCollection AddCategoriesService(this IServiceCollection s
6772
services.AddScoped<ICategoriesService, CategoriesService>();
6873

6974
services.AddAutoMapper(typeof(CategoryMapper));
70-
75+
76+
services.AddScoped<ICategoriesValidator, CategoriesValidator>();
7177
services.AddScoped<IValidator<CreateCategoryDto>, CreateCategoryValidator>();
7278
services.AddScoped<IValidator<UpdateCategoryDto>, UpdateCategoryValidator>();
7379

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using FluentValidation.Results;
2+
using static FinancialHub.Common.Results.Errors.ValidationError;
3+
4+
namespace FinancialHub.Core.Application.Extensions
5+
{
6+
public static class ValidationFailureExtensions
7+
{
8+
public static FieldValidationError[] ToFieldValidationError(this List<ValidationFailure> failures)
9+
{
10+
return failures
11+
.GroupBy(x => x.PropertyName)
12+
.Select(x =>
13+
new FieldValidationError(
14+
field: x.Key,
15+
messages: x.Select(y => y.ErrorMessage).ToArray()
16+
)
17+
).ToArray();
18+
}
19+
}
20+
}

src/api/core/FinancialHub.Core.Application/Services/AccountsService.cs

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,33 @@
11
using AutoMapper;
22
using FinancialHub.Core.Domain.DTOS.Accounts;
3-
using FinancialHub.Core.Domain.Interfaces.Resources;
3+
using FinancialHub.Core.Domain.Interfaces.Validators;
44

55
namespace FinancialHub.Core.Application.Services
66
{
77
public class AccountsService : IAccountsService
88
{
99
private readonly IAccountsProvider provider;
10+
private readonly IAccountsValidator accountValidator;
1011
private readonly IMapper mapper;
11-
private readonly IErrorMessageProvider errorMessageProvider;
1212

13-
public AccountsService(IAccountsProvider provider, IMapper mapper,IErrorMessageProvider errorMessageProvider)
13+
public AccountsService(
14+
IAccountsProvider provider, IAccountsValidator accountValidator,
15+
IMapper mapper
16+
)
1417
{
1518
this.provider = provider;
19+
this.accountValidator = accountValidator;
1620
this.mapper = mapper;
17-
this.errorMessageProvider = errorMessageProvider;
1821
}
1922

2023
public async Task<ServiceResult<AccountDto>> CreateAsync(CreateAccountDto accountDto)
2124
{
25+
var validationResult = await this.accountValidator.ValidateAsync(accountDto);
26+
if(validationResult.HasError)
27+
{
28+
return validationResult.Error;
29+
}
30+
2231
var account = this.mapper.Map<AccountModel>(accountDto);
2332

2433
var result = await this.provider.CreateAsync(account);
@@ -40,33 +49,34 @@ public async Task<ServiceResult<ICollection<AccountDto>>> GetAllAsync()
4049

4150
public async Task<ServiceResult<AccountDto>> GetByIdAsync(Guid id)
4251
{
43-
var existingAccount = await this.provider.GetByIdAsync(id);
44-
if (existingAccount == null)
52+
var accountExists = await this.accountValidator.ExistsAsync(id);
53+
if (accountExists.HasError)
4554
{
46-
return new NotFoundError(
47-
this.errorMessageProvider.NotFoundMessage("Account", id)
48-
);
55+
return accountExists.Error;
4956
}
5057

58+
var existingAccount = await this.provider.GetByIdAsync(id);
59+
5160
return this.mapper.Map<AccountDto>(existingAccount);
5261
}
5362

5463
public async Task<ServiceResult<AccountDto>> UpdateAsync(Guid id, UpdateAccountDto account)
5564
{
56-
var existingAccountResult = await this.GetByIdAsync(id);
57-
if (existingAccountResult.HasError)
65+
var validationResult = await this.accountValidator.ValidateAsync(account);
66+
if (validationResult.HasError)
5867
{
59-
return existingAccountResult.Error;
68+
return validationResult.Error;
6069
}
61-
var accountModel = this.mapper.Map<AccountModel>(account);
6270

63-
var updatedAccount = await this.provider.UpdateAsync(id, accountModel);
64-
if (updatedAccount == null)
71+
validationResult = await this.accountValidator.ExistsAsync(id);
72+
if (validationResult.HasError)
6573
{
66-
return new InvalidDataError(
67-
this.errorMessageProvider.UpdateFailedMessage("Account", id)
68-
);
74+
return validationResult.Error;
6975
}
76+
77+
var accountModel = this.mapper.Map<AccountModel>(account);
78+
79+
var updatedAccount = await this.provider.UpdateAsync(id, accountModel);
7080

7181
return this.mapper.Map<AccountDto>(updatedAccount);
7282
}

src/api/core/FinancialHub.Core.Application/Services/BalancesService.cs

Lines changed: 28 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,35 @@
11
using AutoMapper;
22
using FinancialHub.Core.Domain.DTOS.Balances;
3-
using FinancialHub.Core.Domain.Interfaces.Resources;
3+
using FinancialHub.Core.Domain.Interfaces.Validators;
44

55
namespace FinancialHub.Core.Application.Services
66
{
77
public class BalancesService : IBalancesService
88
{
9-
private readonly IAccountsProvider accountsProvider;
109
private readonly IBalancesProvider balancesProvider;
11-
private readonly IErrorMessageProvider errorMessageProvider;
10+
private readonly IBalancesValidator balancesValidator;
11+
private readonly IAccountsValidator accountsValidator;
1212
private readonly IMapper mapper;
1313

1414
public BalancesService(
1515
IBalancesProvider balancesProvider,
16-
IAccountsProvider accountsProvider,
17-
IErrorMessageProvider errorMessageProvider,
16+
IBalancesValidator balancesValidator, IAccountsValidator accountsValidator,
1817
IMapper mapper
1918
)
2019
{
2120
this.balancesProvider = balancesProvider;
22-
this.accountsProvider = accountsProvider;
23-
this.errorMessageProvider = errorMessageProvider;
21+
this.balancesValidator = balancesValidator;
22+
this.accountsValidator = accountsValidator;
2423
this.mapper = mapper;
2524
}
2625

27-
private async Task<ServiceResult> ValidateAccountAsync(Guid accountId)
28-
{
29-
var account = await this.accountsProvider.GetByIdAsync(accountId);
30-
31-
if (account == null)
32-
{
33-
return new NotFoundError(
34-
this.errorMessageProvider.NotFoundMessage("Account", accountId)
35-
);
36-
}
37-
38-
return new ServiceResult();
39-
}
40-
4126
public async Task<ServiceResult<BalanceDto>> CreateAsync(CreateBalanceDto balance)
4227
{
43-
var validationResult = await this.ValidateAccountAsync(balance.AccountId);
28+
var validationResult = await this.balancesValidator.ValidateAsync(balance);
29+
if (validationResult.HasError)
30+
return validationResult.Error;
31+
32+
validationResult = await this.accountsValidator.ExistsAsync(balance.AccountId);
4433
if (validationResult.HasError)
4534
return validationResult.Error;
4635

@@ -58,20 +47,20 @@ public async Task<ServiceResult<int>> DeleteAsync(Guid id)
5847

5948
public async Task<ServiceResult<BalanceDto>> GetByIdAsync(Guid id)
6049
{
61-
var balance = await this.balancesProvider.GetByIdAsync(id);
62-
if (balance == null)
50+
var validationResult = await this.balancesValidator.ExistsAsync(id);
51+
if (validationResult.HasError)
6352
{
64-
return new NotFoundError(
65-
this.errorMessageProvider.NotFoundMessage("Balance", id)
66-
);
53+
return validationResult.Error;
6754
}
6855

56+
var balance = await this.balancesProvider.GetByIdAsync(id);
57+
6958
return this.mapper.Map<BalanceDto>(balance);
7059
}
7160

7261
public async Task<ServiceResult<ICollection<BalanceDto>>> GetAllByAccountAsync(Guid accountId)
7362
{
74-
var validationResult = await this.ValidateAccountAsync(accountId);
63+
var validationResult = await this.accountsValidator.ExistsAsync(accountId);
7564
if (validationResult.HasError)
7665
{
7766
return validationResult.Error;
@@ -84,17 +73,17 @@ public async Task<ServiceResult<ICollection<BalanceDto>>> GetAllByAccountAsync(G
8473

8574
public async Task<ServiceResult<BalanceDto>> UpdateAsync(Guid id, UpdateBalanceDto balance)
8675
{
87-
var oldBalance = await this.GetByIdAsync(id);
88-
if (oldBalance.HasError)
89-
{
90-
return oldBalance.Error;
91-
}
76+
var validationResult = await this.balancesValidator.ValidateAsync(balance);
77+
if (validationResult.HasError)
78+
return validationResult.Error;
9279

93-
var validationResult = await this.ValidateAccountAsync(balance.AccountId);
80+
validationResult = await this.balancesValidator.ExistsAsync(id);
81+
if (validationResult.HasError)
82+
return validationResult.Error;
83+
84+
validationResult = await this.accountsValidator.ExistsAsync(balance.AccountId);
9485
if (validationResult.HasError)
95-
{
9686
return validationResult.Error;
97-
}
9887

9988
var balanceModel = this.mapper.Map<BalanceModel>(balance);
10089

@@ -105,10 +94,10 @@ public async Task<ServiceResult<BalanceDto>> UpdateAsync(Guid id, UpdateBalanceD
10594

10695
public async Task<ServiceResult<BalanceModel>> UpdateAmountAsync(Guid id, decimal newAmount)
10796
{
108-
var balanceResult = await this.GetByIdAsync(id);
109-
if (balanceResult.HasError)
97+
var oldBalance = await this.balancesValidator.ExistsAsync(id);
98+
if (oldBalance.HasError)
11099
{
111-
return balanceResult.Error;
100+
return oldBalance.Error;
112101
}
113102

114103
return await balancesProvider.UpdateAmountAsync(id, newAmount);

0 commit comments

Comments
 (0)