Skip to content

Commit 363e614

Browse files
RafaelKC¨Rafael
andauthored
FIN-34 Create Wallet CRUD (#18)
* FIN-34 wip wallet crud * FIN-34 configure wallets in context * FIN-34 adding wallet crud and improved title categories crudd and rules * FIN-34 adusted tests * FIN-34 adding wallet tests * FIN-34 addming financial institution to wallet * FIN-34 adding tests to financial institutions * FIN-34 adustes enum name * FIN-34 creating wallet on create user * FIN-34 removing i like because of tests * FIN-34 adjusted --------- Co-authored-by: ¨Rafael <¨rafaelkaua97@gmail.com¨>
1 parent 2dfdaf6 commit 363e614

39 files changed

Lines changed: 3579 additions & 149 deletions

.idea/.idea.Fin-Backend/.idea/dataSources.xml

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/.idea.Fin-Backend/.idea/data_source_mapping.xml

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Fin-Backend.sln.DotSettings.user

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
2+
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ABadHttpRequestException_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F58fd8e539d784853aa0a483642931ebf4c000_003F16_003Fbb4f7d05_003FBadHttpRequestException_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
23
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AModelBuilder_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fd83f823935e343bfaf4ef4c6263b8a12291438_003F33_003Fc1982b24_003FModelBuilder_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
34
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AParameterExpression_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E2_003Fresharper_002Dhost_003FSourcesCache_003F6182caf029c8666b96a8b3bae244ef29b988c59b58e9b3439b794a4f8195bec_003FParameterExpression_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
45
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AType_002ECoreCLR_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E2_003Fresharper_002Dhost_003FSourcesCache_003F7e333a9f3297ba553cccfd3b7c3f1f96125b23d09f883e4d6e66d531559a4c_003FType_002ECoreCLR_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
5-
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=85a9ffa2_002D6d16_002D4824_002Da26f_002De2d6d0602eb2/@EntryIndexedValue">&lt;SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from Solution" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"&gt;
6-
&lt;Solution /&gt;
7-
&lt;/SessionState&gt;</s:String>
6+
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AValidationResult_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fa6e670ad021647bd9cd5d3c28cc553172c800_003F99_003F09c94557_003FValidationResult_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
7+
8+
89

910
</wpf:ResourceDictionary>

Fin.Api/FinancialInstitutions/FinancialInstitutionController.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
using Fin.Application.FinancialInstitutions;
2+
using Fin.Application.FinancialInstitutions.Dtos;
23
using Fin.Domain.FinancialInstitutions.Dtos;
34
using Fin.Domain.Global.Classes;
5+
using Fin.Infrastructure.Authentications.Constants;
46
using Microsoft.AspNetCore.Authorization;
57
using Microsoft.AspNetCore.Mvc;
68

@@ -24,27 +26,31 @@ public async Task<ActionResult<FinancialInstitutionOutput>> Get([FromRoute] Guid
2426
}
2527

2628
[HttpPost]
29+
[Authorize(Roles = AuthenticationRoles.Admin)]
2730
public async Task<ActionResult<FinancialInstitutionOutput>> Create([FromBody] FinancialInstitutionInput input)
2831
{
2932
var institution = await service.Create(input, autoSave: true);
3033
return institution != null ? Created($"financial-institutions/{institution.Id}", institution) : UnprocessableEntity();
3134
}
3235

3336
[HttpPut("{id:guid}")]
37+
[Authorize(Roles = AuthenticationRoles.Admin)]
3438
public async Task<ActionResult> Update([FromRoute] Guid id, [FromBody] FinancialInstitutionInput input)
3539
{
3640
var updated = await service.Update(id, input, autoSave: true);
3741
return updated ? Ok() : NotFound();
3842
}
3943

4044
[HttpDelete("{id:guid}")]
45+
[Authorize(Roles = AuthenticationRoles.Admin)]
4146
public async Task<ActionResult> Delete([FromRoute] Guid id)
4247
{
4348
var deleted = await service.Delete(id, autoSave: true);
4449
return deleted ? Ok() : NotFound();
4550
}
4651

4752
[HttpPatch("{id:guid}/toggle-inactive")]
53+
[Authorize(Roles = AuthenticationRoles.Admin)]
4854
public async Task<ActionResult> ToggleInactive([FromRoute] Guid id)
4955
{
5056
var toggled = await service.ToggleInactive(id, autoSave: true);

Fin.Api/Program.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,26 @@
44
using Fin.Infrastructure.Extensions;
55
using Fin.Infrastructure.Seeders.Extensions;
66
using Hangfire;
7+
using NSwag;
78

89
var builder = WebApplication.CreateBuilder(args);
910

1011
var frontEndUrl = builder.Configuration.GetSection(AppConstants.FrontUrlConfigKey).Get<string>();
1112

1213
builder.Services
1314
.AddInfrastructure(builder.Configuration)
14-
.AddOpenApiDocument()
15+
.AddOpenApiDocument(config =>
16+
{
17+
config.Title = "FinApp API";
18+
config.Version = "v1";
19+
20+
config.AddSecurity("Bearer", [], new OpenApiSecurityScheme
21+
{
22+
Type = OpenApiSecuritySchemeType.Http,
23+
Scheme = "bearer",
24+
BearerFormat = "JWT",
25+
});
26+
})
1527
.AddCors(options =>
1628
{
1729
options.AddPolicy("AllowAngularLocalhost",
Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using Fin.Application.TitleCategories;
22
using Fin.Application.TitleCategories.Dtos;
3+
using Fin.Application.TitleCategories.Enums;
34
using Fin.Domain.Global.Classes;
45
using Fin.Domain.TitleCategories.Dtos;
56
using Microsoft.AspNetCore.Authorization;
@@ -9,46 +10,52 @@ namespace Fin.Api.TitleCategories;
910

1011
[Route("title-categories")]
1112
[Authorize]
12-
public class TitleCategoryController(ITitleCategoryService service): ControllerBase
13+
public class TitleCategoryController(ITitleCategoryService service) : ControllerBase
1314
{
1415
[HttpGet]
1516
public async Task<PagedOutput<TitleCategoryOutput>> GetList([FromQuery] TitleCategoryGetListInput input)
1617
{
1718
return await service.GetList(input);
1819
}
19-
20+
2021
[HttpGet("{id:guid}")]
2122
public async Task<ActionResult<TitleCategoryOutput>> Get([FromRoute] Guid id)
2223
{
2324
var category = await service.Get(id);
24-
return category != null ? Ok(category) : NotFound();
25+
return category != null ? Ok(category) : NotFound();
2526
}
26-
27+
2728
[HttpPost]
2829
public async Task<ActionResult<TitleCategoryOutput>> Create([FromBody] TitleCategoryInput input)
2930
{
30-
var category = await service.Create(input, autoSave: true);
31-
return category != null ? Created($"categories/{category.Id}", category) : UnprocessableEntity();
31+
var validationResult = await service.Create(input, autoSave: true);
32+
return validationResult.Success
33+
? Created($"categories/{validationResult.Data?.Id}", validationResult.Data)
34+
: UnprocessableEntity(validationResult);
3235
}
33-
36+
3437
[HttpPut("{id:guid}")]
3538
public async Task<ActionResult> Update([FromRoute] Guid id, [FromBody] TitleCategoryInput input)
3639
{
37-
var updated = await service.Update(id, input, autoSave: true);
38-
return updated ? Ok() : UnprocessableEntity();
40+
var validationResult = await service.Update(id, input, autoSave: true);
41+
return validationResult.Success
42+
? Ok()
43+
: validationResult.ErrorCode == TitleCategoryCreateOrUpdateErrorCode.TitleCategoryNotFound
44+
? NotFound(validationResult)
45+
: UnprocessableEntity(validationResult);
3946
}
40-
47+
4148
[HttpPut("toggle-inactivated/{id:guid}")]
4249
public async Task<ActionResult> ToggleInactivated([FromRoute] Guid id)
4350
{
4451
var updated = await service.ToggleInactive(id, autoSave: true);
45-
return updated ? Ok() : UnprocessableEntity();
52+
return updated ? Ok() : NotFound();
4653
}
47-
54+
4855
[HttpDelete("{id:guid}")]
4956
public async Task<ActionResult> Delete([FromRoute] Guid id)
5057
{
5158
var deleted = await service.Delete(id, autoSave: true);
52-
return deleted ? Ok() : UnprocessableEntity();
59+
return deleted ? Ok() : NotFound();
5360
}
5461
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
using Fin.Application.Wallets.Dtos;
2+
using Fin.Application.Wallets.Enums;
3+
using Fin.Application.Wallets.Services;
4+
using Fin.Domain.Global.Classes;
5+
using Fin.Domain.Wallets.Dtos;
6+
using Microsoft.AspNetCore.Authorization;
7+
using Microsoft.AspNetCore.Mvc;
8+
9+
namespace Fin.Api.Wallets;
10+
11+
[Route("wallets")]
12+
[Authorize]
13+
public class WalletController(IWalletService service) : ControllerBase
14+
{
15+
[HttpGet]
16+
public async Task<PagedOutput<WalletOutput>> GetList([FromQuery] WalletGetListInput input)
17+
{
18+
return await service.GetList(input);
19+
}
20+
21+
[HttpGet("{id:guid}")]
22+
public async Task<ActionResult<WalletOutput>> Get([FromRoute] Guid id)
23+
{
24+
var category = await service.Get(id);
25+
return category != null ? Ok(category) : NotFound();
26+
}
27+
28+
[HttpPost]
29+
public async Task<ActionResult<WalletOutput>> Create([FromBody] WalletInput input)
30+
{
31+
var validationResult = await service.Create(input, autoSave: true);
32+
return validationResult.Success
33+
? Created($"categories/{validationResult.Data?.Id}", validationResult.Data)
34+
: UnprocessableEntity(validationResult);
35+
}
36+
37+
[HttpPut("{id:guid}")]
38+
public async Task<ActionResult> Update([FromRoute] Guid id, [FromBody] WalletInput input)
39+
{
40+
var validationResult = await service.Update(id, input, autoSave: true);
41+
return validationResult.Success ? Ok() :
42+
validationResult.ErrorCode == WalletCreateOrUpdateErrorCode.WalletNotFound ? NotFound(validationResult) :
43+
UnprocessableEntity(validationResult);
44+
}
45+
46+
[HttpPut("toggle-inactivated/{id:guid}")]
47+
public async Task<ActionResult> ToggleInactivated([FromRoute] Guid id)
48+
{
49+
var validationResult = await service.ToggleInactive(id, autoSave: true);
50+
return validationResult.Success ? Ok() :
51+
validationResult.ErrorCode == WalletToggleInactiveErrorCode.WalletNotFound ? NotFound(validationResult) :
52+
UnprocessableEntity(validationResult);
53+
}
54+
55+
[HttpDelete("{id:guid}")]
56+
public async Task<ActionResult> Delete([FromRoute] Guid id)
57+
{
58+
var validationResult = await service.Delete(id, autoSave: true);
59+
return validationResult.Success ? Ok() :
60+
validationResult.ErrorCode == WalletDeleteErrorCode.WalletNotFound ? NotFound(validationResult) :
61+
UnprocessableEntity(validationResult);
62+
}
63+
}

Fin.Application/FinancialInstitutions/Dtos/FinancialInstitutionGetListInput.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
using Fin.Domain.FinancialInstitutions.Enums;
22
using Fin.Domain.Global.Classes;
33

4+
namespace Fin.Application.FinancialInstitutions.Dtos;
5+
46
public class FinancialInstitutionGetListInput : PagedFilteredAndSortedInput
57
{
68
public bool? Inactive { get; set; }

Fin.Application/FinancialInstitutions/FinancialInstitutionService.cs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using Fin.Application.FinancialInstitutions.Dtos;
12
using Fin.Domain.FinancialInstitutions.Dtos;
23
using Fin.Domain.FinancialInstitutions.Entities;
34
using Fin.Domain.Global.Classes;
@@ -12,12 +13,12 @@ namespace Fin.Application.FinancialInstitutions;
1213

1314
public interface IFinancialInstitutionService
1415
{
15-
Task<FinancialInstitutionOutput> Get(Guid id);
16-
Task<PagedOutput<FinancialInstitutionOutput>> GetList(FinancialInstitutionGetListInput input);
17-
Task<FinancialInstitutionOutput> Create(FinancialInstitutionInput input, bool autoSave = false);
18-
Task<bool> Update(Guid id, FinancialInstitutionInput input, bool autoSave = false);
19-
Task<bool> Delete(Guid id, bool autoSave = false);
20-
Task<bool> ToggleInactive(Guid id, bool autoSave = false);
16+
public Task<FinancialInstitutionOutput> Get(Guid id);
17+
public Task<PagedOutput<FinancialInstitutionOutput>> GetList(FinancialInstitutionGetListInput input);
18+
public Task<FinancialInstitutionOutput> Create(FinancialInstitutionInput input, bool autoSave = false);
19+
public Task<bool> Update(Guid id, FinancialInstitutionInput input, bool autoSave = false);
20+
public Task<bool> Delete(Guid id, bool autoSave = false);
21+
public Task<bool> ToggleInactive(Guid id, bool autoSave = false);
2122
}
2223

2324
public class FinancialInstitutionService(
@@ -70,18 +71,20 @@ public async Task<bool> Update(Guid id, FinancialInstitutionInput input, bool au
7071
public async Task<bool> Delete(Guid id, bool autoSave = false)
7172
{
7273
var institution = await repository.Query()
74+
.Include(f => f.Wallets)
7375
.FirstOrDefaultAsync(f => f.Id == id);
74-
if (institution == null) return false;
75-
76+
if (institution == null || institution.Wallets.Any()) return false;
77+
7678
await repository.DeleteAsync(institution, autoSave);
7779
return true;
7880
}
7981

8082
public async Task<bool> ToggleInactive(Guid id, bool autoSave = false)
8183
{
8284
var institution = await repository.Query()
85+
.Include(f => f.Wallets)
8386
.FirstOrDefaultAsync(f => f.Id == id);
84-
if (institution == null) return false;
87+
if (institution == null || (!institution.Inactive && institution.Wallets.Any(w => !w.Inactivated))) return false;
8588

8689
institution.ToggleInactive();
8790

Fin.Application/Globals/Dtos/ValidationResultDto.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@ public class ValidationResultDto<D, E>
88
public E? ErrorCode { get; set; }
99
}
1010

11-
public class ValidationResultDto<D>
11+
public class ValidationResultDto<D>
1212
{
1313
public D? Data { get; set; }
1414
public string Message { get; set; }
1515
public bool Success { get; set; }
1616
public Enum? ErrorCode { get; set; }
1717
}
18+

0 commit comments

Comments
 (0)