Finalized added puppeteer changes
This commit is contained in:
parent
f32b07b2b8
commit
4c69e4989c
@ -1,42 +1,30 @@
|
|||||||
namespace AAIntegration.SimmonsBank.API.Controllers;
|
namespace AAIntegration.SimmonsBank.API.Controllers;
|
||||||
|
|
||||||
using AutoMapper;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
using AAIntegration.SimmonsBank.API.Models.Accounts;
|
using AAIntegration.SimmonsBank.API.Models.Accounts;
|
||||||
using AAIntegration.SimmonsBank.API.Services;
|
using AAIntegration.SimmonsBank.API.Services;
|
||||||
using AAIntegration.SimmonsBank.API.Config;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using AAIntegration.SimmonsBank.API.Entities;
|
using AAIntegration.SimmonsBank.API.Entities;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Newtonsoft.Json.Linq;
|
using AAIntegration.SimmonsBank.API.Models.Transactions;
|
||||||
|
|
||||||
[Authorize]
|
[Authorize]
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("[controller]")]
|
[Route("[controller]")]
|
||||||
public class AccountsController : ControllerBase
|
public class AccountsController : ControllerBase
|
||||||
{
|
{
|
||||||
private IAccountService _accountService;
|
|
||||||
private IUserService _userService;
|
private IUserService _userService;
|
||||||
private IMapper _mapper;
|
|
||||||
private readonly AppSettings _appSettings;
|
|
||||||
private readonly ILogger<AccountsController> _logger;
|
private readonly ILogger<AccountsController> _logger;
|
||||||
private IPuppeteerService _puppeteerService;
|
private IPuppeteerService _puppeteerService;
|
||||||
|
|
||||||
public AccountsController(
|
public AccountsController(
|
||||||
IAccountService accountService,
|
|
||||||
IUserService userService,
|
IUserService userService,
|
||||||
IMapper mapper,
|
|
||||||
IOptions<AppSettings> appSettings,
|
|
||||||
ILogger<AccountsController> logger,
|
ILogger<AccountsController> logger,
|
||||||
IPuppeteerService puppeteerService)
|
IPuppeteerService puppeteerService)
|
||||||
{
|
{
|
||||||
_accountService = accountService;
|
|
||||||
_userService = userService;
|
_userService = userService;
|
||||||
_mapper = mapper;
|
|
||||||
_appSettings = appSettings.Value;
|
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_puppeteerService = puppeteerService;
|
_puppeteerService = puppeteerService;
|
||||||
}
|
}
|
||||||
@ -44,56 +32,35 @@ public class AccountsController : ControllerBase
|
|||||||
[HttpGet]
|
[HttpGet]
|
||||||
public async Task<IActionResult> GetAllAsync()
|
public async Task<IActionResult> GetAllAsync()
|
||||||
{
|
{
|
||||||
List<AccountDTO> accounts = await _puppeteerService.GetAccounts(_userService.GetUser(User.FindFirstValue(ClaimTypes.NameIdentifier)));
|
List<AccountDTO> accounts = await _puppeteerService.GetAccounts(GetCurrentUser());
|
||||||
|
|
||||||
return Ok(accounts);
|
return Ok(accounts);
|
||||||
/*List<AccountDTO> accountDtos = new List<AccountDTO>();
|
|
||||||
|
|
||||||
foreach (Account acc in _accountService.GetAll(GetCurrentUserId()))
|
|
||||||
accountDtos.Add(_mapper.Map<Account, AccountDTO>(acc));
|
|
||||||
|
|
||||||
return Ok(accountDtos);*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("{id}")]
|
[HttpGet("{account_guid}")]
|
||||||
public IActionResult GetById(int id)
|
public async Task<IActionResult> GetByGUIDAsync(string account_guid)
|
||||||
{
|
{
|
||||||
Account account = _accountService.GetById(id, GetCurrentUserId());
|
return Ok(await GetAccountAsync(account_guid));
|
||||||
return Ok(_mapper.Map<Account, AccountDTO>(account));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost]
|
[HttpGet("{account_guid}/transactions")]
|
||||||
public IActionResult Create([FromBody]AccountCreateRequest model)
|
public async Task<IActionResult> GetTransactionsAsync(string account_guid, uint offset = 0, uint limit = 500)
|
||||||
{
|
{
|
||||||
_accountService.Create(model, GetCurrentUserId());
|
List<TransactionDTO> transactions = await _puppeteerService.GetTransactions(GetCurrentUser(), (await GetAccountAsync(account_guid)).Id, offset, limit);
|
||||||
return Ok(new { message = "account created" });
|
return Ok(transactions);
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPut("{id}")]
|
|
||||||
public IActionResult Update(int id, [FromBody]AccountUpdateRequest model)
|
|
||||||
{
|
|
||||||
_accountService.Update(id, model, GetCurrentUserId());
|
|
||||||
return Ok(new { message = "account updated" });
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpDelete("{id}")]
|
|
||||||
public IActionResult Delete(int id)
|
|
||||||
{
|
|
||||||
_accountService.Delete(id, GetCurrentUserId());
|
|
||||||
return Ok(new { message = "account deleted" });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
|
|
||||||
private int GetCurrentUserId()
|
private async Task<AccountDTO> GetAccountAsync(string account_guid)
|
||||||
|
{
|
||||||
|
List<AccountDTO> accounts = await _puppeteerService.GetAccounts(GetCurrentUser());
|
||||||
|
AccountDTO account = accounts.FirstOrDefault(a => a.Id == account_guid) ?? throw new KeyNotFoundException("Account not found");
|
||||||
|
return account;
|
||||||
|
}
|
||||||
|
|
||||||
|
private User GetCurrentUser()
|
||||||
{
|
{
|
||||||
string apiKey = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
string apiKey = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||||
|
return _userService.GetUser(apiKey);
|
||||||
if (apiKey is null)
|
|
||||||
_logger.LogInformation($"ApiKey: is null");
|
|
||||||
|
|
||||||
_logger.LogInformation($"apiKey: {apiKey}");
|
|
||||||
|
|
||||||
return _userService.GetUser(apiKey).Id;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,109 +0,0 @@
|
|||||||
namespace AAIntegration.SimmonsBank.API.Controllers;
|
|
||||||
|
|
||||||
using AutoMapper;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
using AAIntegration.SimmonsBank.API.Models.Transactions;
|
|
||||||
using AAIntegration.SimmonsBank.API.Services;
|
|
||||||
using AAIntegration.SimmonsBank.API.Config;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using AAIntegration.SimmonsBank.API.Entities;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using System.Security.Claims;
|
|
||||||
|
|
||||||
[Authorize]
|
|
||||||
[ApiController]
|
|
||||||
[Route("[controller]")]
|
|
||||||
public class TransactionsController : ControllerBase
|
|
||||||
{
|
|
||||||
private ITransactionService _transactionService;
|
|
||||||
private IUserService _userService;
|
|
||||||
private IMapper _mapper;
|
|
||||||
private readonly AppSettings _appSettings;
|
|
||||||
private readonly ILogger<TransactionsController> _logger;
|
|
||||||
|
|
||||||
public TransactionsController(
|
|
||||||
ITransactionService transactionService,
|
|
||||||
IUserService userService,
|
|
||||||
IMapper mapper,
|
|
||||||
IOptions<AppSettings> appSettings,
|
|
||||||
ILogger<TransactionsController> logger)
|
|
||||||
{
|
|
||||||
_transactionService = transactionService;
|
|
||||||
_userService = userService;
|
|
||||||
_mapper = mapper;
|
|
||||||
_appSettings = appSettings.Value;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet]
|
|
||||||
public IActionResult GetAll(int? accountId = null)
|
|
||||||
{
|
|
||||||
List<TransactionDto> transactionDtos = new List<TransactionDto>();
|
|
||||||
|
|
||||||
foreach (Transaction tran in _transactionService.GetAll(this.GetCurrentUserId()))
|
|
||||||
{
|
|
||||||
if (accountId.HasValue
|
|
||||||
&& (tran.DebitAccount == null || tran.DebitAccount.Id != accountId)
|
|
||||||
&& (tran.CreditAccount == null || tran.CreditAccount.Id != accountId))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
transactionDtos.Add(_mapper.Map<Transaction, TransactionDto>(tran));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort by Date
|
|
||||||
transactionDtos.Sort((t1, t2) => t2.Date.CompareTo(t1.Date));
|
|
||||||
|
|
||||||
return Ok(transactionDtos);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("{id}")]
|
|
||||||
public IActionResult GetById(int id)
|
|
||||||
{
|
|
||||||
Transaction tran = _transactionService.GetById(id, this.GetCurrentUserId());
|
|
||||||
return Ok(_mapper.Map<Transaction, TransactionDto>(tran));
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost("BulkAdd")]
|
|
||||||
public IActionResult BulkCreate([FromBody]List<TransactionCreate> model)
|
|
||||||
{
|
|
||||||
List<Transaction> trans = _transactionService.BulkCreate(model, this.GetCurrentUserId()).ToList();
|
|
||||||
return Ok(new { message = $"{trans.Count()} transaction(s) created." });
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost]
|
|
||||||
public IActionResult Create([FromBody]TransactionCreate model)
|
|
||||||
{
|
|
||||||
Transaction tran = _transactionService.Create(model, this.GetCurrentUserId());
|
|
||||||
return Ok(new { message = $"transaction '{tran.Description}' created with id '{tran.Id}'." });
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPut("{id}")]
|
|
||||||
public IActionResult Update(int id, [FromBody]TransactionUpdateRequest model)
|
|
||||||
{
|
|
||||||
_transactionService.Update(id, model, this.GetCurrentUserId());
|
|
||||||
return Ok(new { message = $"transaction with id '{id}' updated" });
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpDelete("{id}")]
|
|
||||||
public IActionResult Delete(int id)
|
|
||||||
{
|
|
||||||
_transactionService.Delete(id, this.GetCurrentUserId());
|
|
||||||
return Ok(new { message = "transaction deleted" });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helpers
|
|
||||||
|
|
||||||
private int GetCurrentUserId()
|
|
||||||
{
|
|
||||||
string apiKey = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
|
||||||
|
|
||||||
if (apiKey is null)
|
|
||||||
_logger.LogInformation($"ApiKey: is null");
|
|
||||||
|
|
||||||
_logger.LogInformation($"apiKey: {apiKey}");
|
|
||||||
|
|
||||||
return _userService.GetUser(apiKey).Id;
|
|
||||||
}
|
|
||||||
}
|
|
@ -58,13 +58,6 @@ public class UsersController : ControllerBase
|
|||||||
|
|
||||||
private string GetCurrentUserApiKey()
|
private string GetCurrentUserApiKey()
|
||||||
{
|
{
|
||||||
string apiKey = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
return User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||||
|
|
||||||
if (apiKey is null)
|
|
||||||
_logger.LogInformation($"ApiKey: is null");
|
|
||||||
|
|
||||||
_logger.LogInformation($"apiKey: {apiKey}");
|
|
||||||
|
|
||||||
return apiKey;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,12 +0,0 @@
|
|||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace AAIntegration.SimmonsBank.API.Entities;
|
|
||||||
|
|
||||||
public class Account
|
|
||||||
{
|
|
||||||
public int Id { get; set; }
|
|
||||||
public string Name { get; set; }
|
|
||||||
public decimal Balance { get; set; }
|
|
||||||
public string ExternalAccountNumber { get; set; }
|
|
||||||
public User Owner { get; set; }
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using AAIntegration.SimmonsBank.API.Services;
|
|
||||||
|
|
||||||
namespace AAIntegration.SimmonsBank.API.Entities;
|
|
||||||
|
|
||||||
public class Transaction
|
|
||||||
{
|
|
||||||
public int Id { get; set; }
|
|
||||||
public DateTime Date { get; set; }
|
|
||||||
public DateTime CreatedOn { get; set; }
|
|
||||||
public DateTime UpdatedOn { get; set; }
|
|
||||||
public string ExternalId { get; set; }
|
|
||||||
public string Description { get; set; }
|
|
||||||
public Account? DebitAccount { get; set; }
|
|
||||||
public Account? CreditAccount { get; set; }
|
|
||||||
public decimal Amount { get; set; }
|
|
||||||
public bool IsPending { get; set; }
|
|
||||||
public User Owner { get; set; }
|
|
||||||
}
|
|
@ -3,14 +3,9 @@ namespace AAIntegration.SimmonsBank.API.Config;
|
|||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
using AAIntegration.SimmonsBank.API.Entities;
|
using AAIntegration.SimmonsBank.API.Entities;
|
||||||
using AAIntegration.SimmonsBank.API.Models.Users;
|
using AAIntegration.SimmonsBank.API.Models.Users;
|
||||||
using AAIntegration.SimmonsBank.API.Models.Accounts;
|
|
||||||
using AAIntegration.SimmonsBank.API.Services;
|
|
||||||
using System.Runtime.Serialization;
|
|
||||||
using AAIntegration.SimmonsBank.API.Models.Transactions;
|
|
||||||
|
|
||||||
public class AutoMapperProfile : Profile
|
public class AutoMapperProfile : Profile
|
||||||
{
|
{
|
||||||
|
|
||||||
public AutoMapperProfile()
|
public AutoMapperProfile()
|
||||||
{ // UserUpdateRequest -> User
|
{ // UserUpdateRequest -> User
|
||||||
CreateMap<UserUpdateRequest, User>()
|
CreateMap<UserUpdateRequest, User>()
|
||||||
@ -26,55 +21,5 @@ public class AutoMapperProfile : Profile
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
|
|
||||||
// AccountUpdateRequest -> Account
|
|
||||||
CreateMap<AccountUpdateRequest, Account>();
|
|
||||||
|
|
||||||
// AccountCreateRequest -> Account
|
|
||||||
CreateMap<AccountCreateRequest, Account>();
|
|
||||||
/*.ForMember(
|
|
||||||
dest => dest.OwnerId,
|
|
||||||
opt => opt.MapFrom(src => src.Owner)
|
|
||||||
);
|
|
||||||
/*.ForAllMembers(x => x.Condition(
|
|
||||||
(src, dest, prop) =>
|
|
||||||
{
|
|
||||||
// ignore both null & empty string properties
|
|
||||||
if (prop == null) return false;
|
|
||||||
if (prop.GetType() == typeof(string) && string.IsNullOrEmpty((string)prop)) return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
))*/
|
|
||||||
|
|
||||||
// Account -> AccountGet
|
|
||||||
CreateMap<Account, AccountDTO>()
|
|
||||||
.ForAllMembers(x => x.Condition(
|
|
||||||
(src, dest, prop) =>
|
|
||||||
{
|
|
||||||
// ignore both null & empty string properties
|
|
||||||
if (prop == null) return false;
|
|
||||||
if (prop.GetType() == typeof(string) && string.IsNullOrEmpty((string)prop)) return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
));
|
|
||||||
|
|
||||||
|
|
||||||
// Transaction -> TransactionDto
|
|
||||||
CreateMap<Transaction, TransactionDto>()
|
|
||||||
.ForMember(dest => dest.DebitAccountId, opt => opt.MapFrom(src => src.DebitAccount.Id))
|
|
||||||
.ForMember(dest => dest.CreditAccountId, opt => opt.MapFrom(src => src.CreditAccount.Id))
|
|
||||||
.ForAllMembers(x => x.Condition(
|
|
||||||
(src, dest, prop) =>
|
|
||||||
{
|
|
||||||
// ignore both null & empty string properties
|
|
||||||
if (prop == null) return false;
|
|
||||||
if (prop.GetType() == typeof(string) && string.IsNullOrEmpty((string)prop)) return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
));
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -31,6 +31,4 @@ public class DataContext : DbContext
|
|||||||
}*/
|
}*/
|
||||||
|
|
||||||
public DbSet<User> Users { get; set; }
|
public DbSet<User> Users { get; set; }
|
||||||
public DbSet<Account> Accounts { get; set; }
|
|
||||||
public DbSet<Transaction> Transactions { get; set; }
|
|
||||||
}
|
}
|
@ -1,13 +0,0 @@
|
|||||||
namespace AAIntegration.SimmonsBank.API.Models.Accounts;
|
|
||||||
|
|
||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using AAIntegration.SimmonsBank.API.Entities;
|
|
||||||
|
|
||||||
public class AccountCreateRequest
|
|
||||||
{
|
|
||||||
public string Name { get; set; }
|
|
||||||
public string InitialBalance { get; set; }
|
|
||||||
public int Currency { get; set; }
|
|
||||||
public string ExternalAccountNumber { get; set; }
|
|
||||||
}
|
|
@ -12,4 +12,8 @@ public class AccountDTO
|
|||||||
public string Numbers { get; set; }
|
public string Numbers { get; set; }
|
||||||
public decimal? Balance { get; set; }
|
public decimal? Balance { get; set; }
|
||||||
public decimal? AvailableBalance { get; set; }
|
public decimal? AvailableBalance { get; set; }
|
||||||
|
public string AccountType { get; set; }
|
||||||
|
public string AccountSubType { get; set; }
|
||||||
|
public DateTime? PaymentDueDate { get; set; }
|
||||||
|
public decimal? PaymentDueAmount { get; set; }
|
||||||
}
|
}
|
@ -1,8 +0,0 @@
|
|||||||
namespace AAIntegration.SimmonsBank.API.Models.Accounts;
|
|
||||||
|
|
||||||
public class AccountUpdateRequest
|
|
||||||
{
|
|
||||||
public string? Name { get; set; } = null;
|
|
||||||
public string? Balance { get; set; } = null;
|
|
||||||
public string? ExternalAccountNumber { get; set; } = null;
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.Runtime;
|
|
||||||
using AAIntegration.SimmonsBank.API.Services;
|
|
||||||
|
|
||||||
namespace AAIntegration.SimmonsBank.API.Models.Transactions;
|
|
||||||
|
|
||||||
public class TransactionCreate
|
|
||||||
{
|
|
||||||
public DateTime Date { get; set; }
|
|
||||||
public string ExternalId { get; set; }
|
|
||||||
public string Description { get; set; }
|
|
||||||
public int? DebitAccount { get; set; }
|
|
||||||
public int? CreditAccount { get; set; }
|
|
||||||
public decimal Amount { get; set; }
|
|
||||||
public bool IsPending { get; set; }
|
|
||||||
}
|
|
@ -0,0 +1,21 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime;
|
||||||
|
using AAIntegration.SimmonsBank.API.Services;
|
||||||
|
|
||||||
|
namespace AAIntegration.SimmonsBank.API.Models.Transactions;
|
||||||
|
|
||||||
|
public class TransactionDTO
|
||||||
|
{
|
||||||
|
public string Id { get; set; }
|
||||||
|
public string AccountId { get; set; }
|
||||||
|
public string Type { get; set; }
|
||||||
|
public decimal? Amount { get; set; }
|
||||||
|
public decimal? RunningBalance { get; set; }
|
||||||
|
public DateTime? DatePosted { get; set; }
|
||||||
|
public DateTime? Date { get; set; }
|
||||||
|
public DateTime? LastUpdated { get; set; }
|
||||||
|
public string PendingStatus { get; set; }
|
||||||
|
public string Memo { get; set; }
|
||||||
|
public string FilteredMemo { get; set; }
|
||||||
|
public string DisplayName { get; set; }
|
||||||
|
}
|
@ -1,19 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.Runtime;
|
|
||||||
using AAIntegration.SimmonsBank.API.Services;
|
|
||||||
|
|
||||||
namespace AAIntegration.SimmonsBank.API.Models.Transactions;
|
|
||||||
|
|
||||||
public class TransactionDto
|
|
||||||
{
|
|
||||||
public int Id { get; set; }
|
|
||||||
public DateTime Date { get; set; }
|
|
||||||
public DateTime CreatedOn { get; set; }
|
|
||||||
public DateTime UpdatedOn { get; set; }
|
|
||||||
public string ExternalId { get; set; }
|
|
||||||
public string Description { get; set; }
|
|
||||||
public int DebitAccountId { get; set; }
|
|
||||||
public int CreditAccountId { get; set; }
|
|
||||||
public decimal Amount { get; set; }
|
|
||||||
public bool IsPending { get; set; }
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
using AAIntegration.SimmonsBank.API.Entities;
|
|
||||||
|
|
||||||
namespace AAIntegration.SimmonsBank.API.Models.Transactions;
|
|
||||||
|
|
||||||
public class TransactionUpdateRequest
|
|
||||||
{
|
|
||||||
public DateTime? Date { get; set; } = null;
|
|
||||||
public string? ExternalId { get; set; } = null;
|
|
||||||
public string? Description { get; set; } = null;
|
|
||||||
public int? DebitAccount { get; set; } = null;
|
|
||||||
public int? CreditAccount { get; set; } = null;
|
|
||||||
public decimal? Amount { get; set; } = null;
|
|
||||||
public bool? IsPending { get; set; } = null;
|
|
||||||
}
|
|
@ -46,16 +46,16 @@ public class PuppeteerProcess : IPuppeteerProcess
|
|||||||
|
|
||||||
public async Task StayLoggedIn(User user)
|
public async Task StayLoggedIn(User user)
|
||||||
{
|
{
|
||||||
_logger.LogInformation($"... doing work and processing for user {user.Id} ...");
|
string prefix = $"Task::StayLoggedIn - {user.Id} - ";
|
||||||
|
|
||||||
if (!await _puppeteerService.IsLoggedIn(user, _stoppingToken))
|
if (!await _puppeteerService.IsLoggedIn(user, _stoppingToken))
|
||||||
{
|
{
|
||||||
_logger.LogInformation("User determined to not be logged in");
|
_logger.LogInformation(prefix + "User is not logged in");
|
||||||
await _puppeteerService.Login(user, _stoppingToken);
|
await _puppeteerService.Login(user, _stoppingToken);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_logger.LogInformation("User is already logged in");
|
_logger.LogInformation(prefix + "User is still logged in");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,8 +80,6 @@ internal class Program
|
|||||||
opt.UseNpgsql(dbConfig.GetConnectionString()));
|
opt.UseNpgsql(dbConfig.GetConnectionString()));
|
||||||
|
|
||||||
builder.Services.AddScoped<IUserService, UserService>();
|
builder.Services.AddScoped<IUserService, UserService>();
|
||||||
builder.Services.AddScoped<IAccountService, AccountService>();
|
|
||||||
builder.Services.AddScoped<ITransactionService, TransactionService>();
|
|
||||||
builder.Services.AddScoped<ICacheService, CacheService>();
|
builder.Services.AddScoped<ICacheService, CacheService>();
|
||||||
builder.Services.AddScoped<IVersionService, VersionService>();
|
builder.Services.AddScoped<IVersionService, VersionService>();
|
||||||
builder.Services.AddScoped<IPuppeteerService, PuppeteerService>();
|
builder.Services.AddScoped<IPuppeteerService, PuppeteerService>();
|
||||||
|
@ -1,132 +0,0 @@
|
|||||||
namespace AAIntegration.SimmonsBank.API.Services;
|
|
||||||
|
|
||||||
using AutoMapper;
|
|
||||||
using BCrypt.Net;
|
|
||||||
using AAIntegration.SimmonsBank.API.Entities;
|
|
||||||
using AAIntegration.SimmonsBank.API.Config;
|
|
||||||
using AAIntegration.SimmonsBank.API.Models.Accounts;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System;
|
|
||||||
using Internal;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
|
|
||||||
public interface IAccountService
|
|
||||||
{
|
|
||||||
IEnumerable<Account> GetAll(int ownerId);
|
|
||||||
Account GetById(int accountId, int ownerId);
|
|
||||||
void Create(AccountCreateRequest model, int ownerId);
|
|
||||||
void Update(int accountId, AccountUpdateRequest model, int ownerId);
|
|
||||||
void Delete(int accountId, int ownerId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class AccountService : IAccountService
|
|
||||||
{
|
|
||||||
private DataContext _context;
|
|
||||||
private readonly IMapper _mapper;
|
|
||||||
private IUserService _userService;
|
|
||||||
|
|
||||||
public AccountService(
|
|
||||||
DataContext context,
|
|
||||||
IMapper mapper,
|
|
||||||
IUserService userService)
|
|
||||||
{
|
|
||||||
_context = context;
|
|
||||||
_mapper = mapper;
|
|
||||||
_userService = userService;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<Account> GetAll(int ownerId)
|
|
||||||
{
|
|
||||||
return _context.Accounts
|
|
||||||
.Include(x => x.Owner)
|
|
||||||
.Where(x => x.Owner.Id == ownerId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Account GetById(int accountId, int ownerId)
|
|
||||||
{
|
|
||||||
return getAccount(accountId, ownerId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Create(AccountCreateRequest model, int ownerId)
|
|
||||||
{
|
|
||||||
// Check that account with same name or same external number doesn't exist
|
|
||||||
IEnumerable<Account> accountsWithSameName = _context.Accounts
|
|
||||||
.Include(x => x.Owner)
|
|
||||||
.Where(x => x.Name.ToUpper() == model.Name.ToUpper() && x.Owner.Id == ownerId);
|
|
||||||
|
|
||||||
if (accountsWithSameName.Count() > 0)
|
|
||||||
throw new AppException("Account with name '" + model.Name + "' already exists");
|
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(model.ExternalAccountNumber))
|
|
||||||
{
|
|
||||||
IEnumerable<Account> matches = _context.Accounts
|
|
||||||
.Include(x => x.Owner)
|
|
||||||
.Where(x => x.ExternalAccountNumber == model.ExternalAccountNumber && x.Owner.Id == ownerId);
|
|
||||||
|
|
||||||
if (matches.Count() > 0)
|
|
||||||
throw new AppException("Account with external account number '" + model.ExternalAccountNumber + "' already exists under account named '" + matches.First().Name + "'");
|
|
||||||
}
|
|
||||||
|
|
||||||
Account account = new Account {
|
|
||||||
Name = model.Name,
|
|
||||||
Balance = Convert.ToDecimal(model.InitialBalance),
|
|
||||||
ExternalAccountNumber = model.ExternalAccountNumber,
|
|
||||||
Owner = getOwner(ownerId)
|
|
||||||
};
|
|
||||||
|
|
||||||
_context.Accounts.Add(account);
|
|
||||||
_context.SaveChanges();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Update(int accountId, AccountUpdateRequest model, int ownerId)
|
|
||||||
{
|
|
||||||
Account account = getAccount(accountId, ownerId);
|
|
||||||
|
|
||||||
// validate
|
|
||||||
if (model.Name != account.Name && _context.Accounts
|
|
||||||
.Include(x => x.Owner)
|
|
||||||
.Any(x => x.Name == model.Name && x.Owner.Id == ownerId))
|
|
||||||
throw new AppException("Account with the name '" + model.Name + "' already exists");
|
|
||||||
|
|
||||||
// Name
|
|
||||||
if (!string.IsNullOrWhiteSpace(model.Name))
|
|
||||||
account.Name = model.Name;
|
|
||||||
|
|
||||||
// External Account Number
|
|
||||||
if (!string.IsNullOrWhiteSpace(model.ExternalAccountNumber))
|
|
||||||
account.ExternalAccountNumber = model.ExternalAccountNumber;
|
|
||||||
|
|
||||||
_context.Accounts.Update(account);
|
|
||||||
_context.SaveChanges();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Delete(int accountId, int ownerId)
|
|
||||||
{
|
|
||||||
var account = getAccount(accountId, ownerId);
|
|
||||||
_context.Accounts.Remove(account);
|
|
||||||
_context.SaveChanges();
|
|
||||||
}
|
|
||||||
|
|
||||||
// helper methods
|
|
||||||
|
|
||||||
private Account getAccount(int id, int ownerId)
|
|
||||||
{
|
|
||||||
var account = _context.Accounts
|
|
||||||
.Include(x => x.Owner)
|
|
||||||
.FirstOrDefault(x => x.Id == id && x.Owner.Id == ownerId);
|
|
||||||
if (account == null) throw new KeyNotFoundException("Account not found");
|
|
||||||
return account;
|
|
||||||
}
|
|
||||||
|
|
||||||
private User getOwner(int ownerId)
|
|
||||||
{
|
|
||||||
User? owner = _context.Users.Find(ownerId);
|
|
||||||
|
|
||||||
if (owner == null)
|
|
||||||
throw new AppException($"Owner with ID of '{ownerId}' could not be found");
|
|
||||||
|
|
||||||
return owner;
|
|
||||||
}
|
|
||||||
}
|
|
@ -24,17 +24,19 @@ using NuGet.Protocol;
|
|||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using NuGet.Protocol.Core.Types;
|
using NuGet.Protocol.Core.Types;
|
||||||
using AAIntegration.SimmonsBank.API.Models.Accounts;
|
using AAIntegration.SimmonsBank.API.Models.Accounts;
|
||||||
|
using AAIntegration.SimmonsBank.API.Models.Transactions;
|
||||||
|
|
||||||
public interface IPuppeteerService
|
public interface IPuppeteerService
|
||||||
{
|
{
|
||||||
Task<bool> Login(User user, CancellationToken cancellationToken);
|
Task<bool> Login(User user, CancellationToken cancellationToken);
|
||||||
Task<bool> IsLoggedIn(User user, CancellationToken cancellationToken);
|
Task<bool> IsLoggedIn(User user, CancellationToken cancellationToken);
|
||||||
Task<List<AccountDTO>> GetAccounts(User user);
|
Task<List<AccountDTO>> GetAccounts(User user);
|
||||||
|
Task<List<TransactionDTO>> GetTransactions(User user, string accountGuid, uint offset = 0, uint limit = 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class PuppeteerService : IPuppeteerService
|
public class PuppeteerService : IPuppeteerService
|
||||||
{
|
{
|
||||||
private const string API_BASE_PATH = "/a/consumer/api";
|
private const string API_BASE_PATH = "/a/consumer/api/v0";
|
||||||
private const string DASHBOARD_SELECTOR = "body > banno-web > bannoweb-layout > bannoweb-dashboard";
|
private const string DASHBOARD_SELECTOR = "body > banno-web > bannoweb-layout > bannoweb-dashboard";
|
||||||
private const string USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36";
|
private const string USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36";
|
||||||
private readonly PuppeteerConfig _config;
|
private readonly PuppeteerConfig _config;
|
||||||
@ -65,6 +67,7 @@ public class PuppeteerService : IPuppeteerService
|
|||||||
|
|
||||||
public async Task<bool> Login(User user, CancellationToken cancellationToken)
|
public async Task<bool> Login(User user, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
string prefix = $"Task::Login - {user.Id} - ";
|
||||||
TimeSpan timeout = TimeSpan.FromSeconds(_config.BrowserOperationTimeoutSeconds);
|
TimeSpan timeout = TimeSpan.FromSeconds(_config.BrowserOperationTimeoutSeconds);
|
||||||
|
|
||||||
// Setup Page
|
// Setup Page
|
||||||
@ -105,7 +108,7 @@ public class PuppeteerService : IPuppeteerService
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_logger.LogError("Failed to find Sign-In button");
|
_logger.LogError(prefix + "Failed to find Sign-In button");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,7 +134,6 @@ public class PuppeteerService : IPuppeteerService
|
|||||||
//IPage page = sender as IPage;
|
//IPage page = sender as IPage;
|
||||||
page.Response -= LoginResponseHandler;
|
page.Response -= LoginResponseHandler;
|
||||||
|
|
||||||
_logger.LogInformation("-----PARSING JSON-----");
|
|
||||||
JToken json = await args.Response.JsonAsync<JToken>();
|
JToken json = await args.Response.JsonAsync<JToken>();
|
||||||
string userId = json["id"].Value<string>();
|
string userId = json["id"].Value<string>();
|
||||||
_cacheService.SetCachedUserValue<string>(user, PuppeteerConstants.USER_SB_ID, userId);
|
_cacheService.SetCachedUserValue<string>(user, PuppeteerConstants.USER_SB_ID, userId);
|
||||||
@ -147,7 +149,7 @@ public class PuppeteerService : IPuppeteerService
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_logger.LogError("Failed to find Verify button");
|
_logger.LogError(prefix + "Failed to find Verify button");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,21 +159,22 @@ public class PuppeteerService : IPuppeteerService
|
|||||||
}
|
}
|
||||||
catch(TimeoutException)
|
catch(TimeoutException)
|
||||||
{
|
{
|
||||||
_logger.LogWarning($"Dashboard isn't loading after login for user '{user.Id}'");
|
_logger.LogWarning(prefix + $"Dashboard isn't loading after login");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.LogInformation($"Dashboard found for '{user.Id}'");
|
_logger.LogInformation(prefix + $"Login success");
|
||||||
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (TaskCanceledException)
|
catch (TaskCanceledException)
|
||||||
{
|
{
|
||||||
_logger.LogError($"Login Task for user '{user.Id}' was canceled");
|
_logger.LogError(prefix + $"Login Task was canceled");
|
||||||
}
|
}
|
||||||
catch (TimeoutException ex)
|
catch (TimeoutException ex)
|
||||||
{
|
{
|
||||||
//_logger.LogWarning($"Login Task timed out for user '{user.Id}' after {timeout} seconds");
|
//_logger.LogWarning($"Login Task timed out for user '{user.Id}' after {timeout} seconds");
|
||||||
_logger.LogError(0, ex, $"Login Task timed out for user '{user.Id}' after {timeout} seconds");
|
_logger.LogError(0, ex, prefix + $"Login Task timed out after {timeout} seconds");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
@ -179,8 +182,7 @@ public class PuppeteerService : IPuppeteerService
|
|||||||
await page.CloseAsync();
|
await page.CloseAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.LogInformation($"Login completed for user {user.Id}");
|
return false;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> IsLoggedIn(User user, CancellationToken cancellationToken)
|
public async Task<bool> IsLoggedIn(User user, CancellationToken cancellationToken)
|
||||||
@ -191,7 +193,7 @@ public class PuppeteerService : IPuppeteerService
|
|||||||
string userSbId = _cacheService.GetCachedUserValue<string>(user, PuppeteerConstants.USER_SB_ID, "");
|
string userSbId = _cacheService.GetCachedUserValue<string>(user, PuppeteerConstants.USER_SB_ID, "");
|
||||||
if (string.IsNullOrWhiteSpace(userSbId))
|
if (string.IsNullOrWhiteSpace(userSbId))
|
||||||
{
|
{
|
||||||
_logger.LogInformation(prefix + $"User SimmonsBank ID not found. User is not logged in.");
|
//_logger.LogInformation(prefix + $"User SimmonsBank ID not found. User is not logged in.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,22 +209,21 @@ public class PuppeteerService : IPuppeteerService
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
IResponse response = await page.GoToAsync(url).WaitAsync(TimeSpan.FromSeconds(_config.BrowserOperationTimeoutSeconds), cancellationToken);
|
IResponse response = await page.GoToAsync(url).WaitAsync(TimeSpan.FromSeconds(_config.BrowserOperationTimeoutSeconds), cancellationToken);
|
||||||
_logger.LogInformation(prefix + $"Request response code '{response.Status}'. Url: '{url}'");
|
//_logger.LogInformation(prefix + $"Request response code '{response.Status}'. Url: '{url}'");
|
||||||
return response.Status == System.Net.HttpStatusCode.OK;
|
return response.Status == System.Net.HttpStatusCode.OK;
|
||||||
}
|
}
|
||||||
catch(TaskCanceledException)
|
catch(TaskCanceledException)
|
||||||
{
|
{
|
||||||
_logger.LogWarning(prefix + $"Task was canceled");
|
_logger.LogError(prefix + $"Task was canceled");
|
||||||
}
|
}
|
||||||
catch(TimeoutException)
|
catch(TimeoutException)
|
||||||
{
|
{
|
||||||
_logger.LogWarning(prefix + $"Request to '{url}' timed out");
|
_logger.LogError(prefix + $"Request to '{url}' timed out");
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public async Task<List<AccountDTO>> GetAccounts(User user)
|
public async Task<List<AccountDTO>> GetAccounts(User user)
|
||||||
{
|
{
|
||||||
string prefix = $"Task::GetAccounts - {user.Id} - ";
|
string prefix = $"Task::GetAccounts - {user.Id} - ";
|
||||||
@ -231,7 +232,7 @@ public class PuppeteerService : IPuppeteerService
|
|||||||
string userSbId = _cacheService.GetCachedUserValue<string>(user, PuppeteerConstants.USER_SB_ID, "");
|
string userSbId = _cacheService.GetCachedUserValue<string>(user, PuppeteerConstants.USER_SB_ID, "");
|
||||||
if (string.IsNullOrWhiteSpace(userSbId))
|
if (string.IsNullOrWhiteSpace(userSbId))
|
||||||
{
|
{
|
||||||
_logger.LogInformation(prefix + $"User SimmonsBank ID not found. User is not logged in.");
|
_logger.LogWarning(prefix + $"User SimmonsBank ID not found. User is not logged in.");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -247,7 +248,7 @@ public class PuppeteerService : IPuppeteerService
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
IResponse response = await page.GoToAsync(url).WaitAsync(TimeSpan.FromSeconds(_config.BrowserOperationTimeoutSeconds));
|
IResponse response = await page.GoToAsync(url).WaitAsync(TimeSpan.FromSeconds(_config.BrowserOperationTimeoutSeconds));
|
||||||
_logger.LogInformation(prefix + $"Request response code '{response.Status}'. Url: '{url}'");
|
//_logger.LogInformation(prefix + $"Request response code '{response.Status}'. Url: '{url}'");
|
||||||
|
|
||||||
if (response.Status == System.Net.HttpStatusCode.OK)
|
if (response.Status == System.Net.HttpStatusCode.OK)
|
||||||
{
|
{
|
||||||
@ -259,11 +260,57 @@ public class PuppeteerService : IPuppeteerService
|
|||||||
}
|
}
|
||||||
catch(TaskCanceledException)
|
catch(TaskCanceledException)
|
||||||
{
|
{
|
||||||
_logger.LogWarning(prefix + $"Task was canceled");
|
_logger.LogError(prefix + $"Task was canceled");
|
||||||
}
|
}
|
||||||
catch(TimeoutException)
|
catch(TimeoutException)
|
||||||
{
|
{
|
||||||
_logger.LogWarning(prefix + $"Request to '{url}' timed out");
|
_logger.LogError(prefix + $"Request to '{url}' timed out");
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<TransactionDTO>> GetTransactions(User user, string accountGuid, uint offset = 0, uint limit = 500)
|
||||||
|
{
|
||||||
|
string prefix = $"Task::GetTransactions - {user.Id} - ";
|
||||||
|
|
||||||
|
// Get User ID
|
||||||
|
string userSbId = _cacheService.GetCachedUserValue<string>(user, PuppeteerConstants.USER_SB_ID, "");
|
||||||
|
if (string.IsNullOrWhiteSpace(userSbId))
|
||||||
|
{
|
||||||
|
_logger.LogWarning(prefix + $"User SimmonsBank ID not found. User is not logged in.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup Page
|
||||||
|
IBrowser browser = await GetUserBrowserAsync(user, new CancellationToken());
|
||||||
|
await using IPage page = await browser.NewPageAsync();
|
||||||
|
await page.SetUserAgentAsync(USER_AGENT);
|
||||||
|
await page.SetViewportAsync(new ViewPortOptions { Width = 1200, Height = 720 });
|
||||||
|
|
||||||
|
// Fetch transactions
|
||||||
|
string url = _config.SimmonsBankBaseUrl + API_BASE_PATH + $"/users/{userSbId}/accounts/{accountGuid}/transactions?offset={offset}&limit={limit}";
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
IResponse response = await page.GoToAsync(url).WaitAsync(TimeSpan.FromSeconds(_config.BrowserOperationTimeoutSeconds));
|
||||||
|
//_logger.LogInformation(prefix + $"Request response code '{response.Status}'. Url: '{url}'");
|
||||||
|
|
||||||
|
if (response.Status == System.Net.HttpStatusCode.OK)
|
||||||
|
{
|
||||||
|
JToken transactions = await response.JsonAsync<JToken>();
|
||||||
|
return transactions.SelectToken("transactions").ToObject<List<TransactionDTO>>();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
_logger.LogError(prefix + $"Received unexpected status code '{response.Status}'");
|
||||||
|
}
|
||||||
|
catch(TaskCanceledException)
|
||||||
|
{
|
||||||
|
_logger.LogError(prefix + $"Task was canceled");
|
||||||
|
}
|
||||||
|
catch(TimeoutException)
|
||||||
|
{
|
||||||
|
_logger.LogError(prefix + $"Request to '{url}' timed out");
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@ -278,7 +325,7 @@ public class PuppeteerService : IPuppeteerService
|
|||||||
if (cachedBrowser != null)
|
if (cachedBrowser != null)
|
||||||
return cachedBrowser;
|
return cachedBrowser;
|
||||||
|
|
||||||
_logger.LogInformation($"Could NOT find the browser for user with id '{user.Id}'. About to create one...");
|
//_logger.LogInformation($"Could NOT find the browser for user with id '{user.Id}'. About to create one...");
|
||||||
|
|
||||||
using var browserFetcher = new BrowserFetcher();
|
using var browserFetcher = new BrowserFetcher();
|
||||||
await browserFetcher.DownloadAsync().WaitAsync(TimeSpan.FromSeconds(_config.BrowserOperationTimeoutSeconds * 20), cancellationToken);
|
await browserFetcher.DownloadAsync().WaitAsync(TimeSpan.FromSeconds(_config.BrowserOperationTimeoutSeconds * 20), cancellationToken);
|
||||||
|
@ -1,230 +0,0 @@
|
|||||||
namespace AAIntegration.SimmonsBank.API.Services;
|
|
||||||
|
|
||||||
using AutoMapper;
|
|
||||||
using BCrypt.Net;
|
|
||||||
using AAIntegration.SimmonsBank.API.Entities;
|
|
||||||
using AAIntegration.SimmonsBank.API.Config;
|
|
||||||
using AAIntegration.SimmonsBank.API.Models.Transactions;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Internal;
|
|
||||||
using System.Collections.Immutable;
|
|
||||||
|
|
||||||
public interface ITransactionService
|
|
||||||
{
|
|
||||||
IEnumerable<Transaction> GetAll(int ownerId);
|
|
||||||
Transaction GetById(int id, int ownerId);
|
|
||||||
IEnumerable<Transaction> BulkCreate(List<TransactionCreate> model, int ownerId);
|
|
||||||
Transaction Create(TransactionCreate model, int ownerId, bool errorOnFail = true);
|
|
||||||
void Update(int id, TransactionUpdateRequest model, int ownerId);
|
|
||||||
void Delete(int id, int ownerId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class TransactionService : ITransactionService
|
|
||||||
{
|
|
||||||
private DataContext _context;
|
|
||||||
private readonly IMapper _mapper;
|
|
||||||
private readonly ILogger<TransactionService> _logger;
|
|
||||||
|
|
||||||
public TransactionService(
|
|
||||||
DataContext context,
|
|
||||||
IMapper mapper,
|
|
||||||
ILogger<TransactionService> logger)
|
|
||||||
{
|
|
||||||
_context = context;
|
|
||||||
_mapper = mapper;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<Transaction> GetAll(int ownerId)
|
|
||||||
{
|
|
||||||
return _context.Transactions
|
|
||||||
.Include(t => t.DebitAccount)
|
|
||||||
.Include(t => t.CreditAccount)
|
|
||||||
.Include(t => t.Owner)
|
|
||||||
.Where(x => x.Owner.Id == ownerId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Transaction GetById(int id, int ownerId)
|
|
||||||
{
|
|
||||||
return getTransaction(id, ownerId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Account prepareAccount(int? accountId)
|
|
||||||
{
|
|
||||||
if (accountId == null || accountId.Value == 0)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Account account = _context.Accounts.Find(accountId.Value);
|
|
||||||
if (account == null)
|
|
||||||
throw new AppException("Could not find account with ID of '" + accountId.Value + "'.");
|
|
||||||
|
|
||||||
return account;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<Transaction> BulkCreate(List<TransactionCreate> model, int ownerId)
|
|
||||||
{
|
|
||||||
List<Transaction> transactions = new List<Transaction>();
|
|
||||||
|
|
||||||
foreach (TransactionCreate tr in model)
|
|
||||||
{
|
|
||||||
var tran = this.Create(tr, ownerId, false);
|
|
||||||
if (tran != null)
|
|
||||||
transactions.Add(tran);
|
|
||||||
}
|
|
||||||
|
|
||||||
return transactions;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Transaction Create(TransactionCreate model, int ownerId, bool errorOnFail = true)
|
|
||||||
{
|
|
||||||
Transaction transaction = new Transaction {
|
|
||||||
Description = model.Description,
|
|
||||||
Date = model.Date.Date.ToUniversalTime(),
|
|
||||||
CreatedOn = DateTime.UtcNow,
|
|
||||||
UpdatedOn = DateTime.UtcNow,
|
|
||||||
ExternalId = string.IsNullOrWhiteSpace(model.ExternalId) ? "" : model.ExternalId,
|
|
||||||
DebitAccount = prepareAccount(model.DebitAccount),
|
|
||||||
CreditAccount = prepareAccount(model.CreditAccount),
|
|
||||||
Amount = Convert.ToDecimal(model.Amount),
|
|
||||||
Owner = this.getOwner(ownerId),
|
|
||||||
IsPending = model.IsPending
|
|
||||||
};
|
|
||||||
|
|
||||||
if (this.ValidateTransaction(transaction, ownerId, errorOnFail) == false)
|
|
||||||
{
|
|
||||||
_logger.LogInformation($"Aborted adding transaction '{transaction.Description}'.");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// At this point transaction itself is valid
|
|
||||||
|
|
||||||
_context.Transactions.Add(transaction);
|
|
||||||
_context.SaveChanges();
|
|
||||||
|
|
||||||
_logger.LogInformation("New transaction successfully created.");
|
|
||||||
|
|
||||||
return transaction;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Update(int id, TransactionUpdateRequest model, int ownerId)
|
|
||||||
{
|
|
||||||
Transaction transaction = getTransaction(id, ownerId);
|
|
||||||
|
|
||||||
// Transaction.Date
|
|
||||||
if (model.Date.HasValue)
|
|
||||||
transaction.Date = model.Date.Value;
|
|
||||||
|
|
||||||
// Transaction.ExternalId
|
|
||||||
if (model.ExternalId != null)
|
|
||||||
transaction.ExternalId = model.ExternalId;
|
|
||||||
|
|
||||||
// Transaction.Description
|
|
||||||
if (model.Description != null)
|
|
||||||
transaction.Description = model.Description;
|
|
||||||
|
|
||||||
// Transaction.DebitAccount
|
|
||||||
if (model.DebitAccount.HasValue)
|
|
||||||
transaction.DebitAccount = prepareAccount(model.DebitAccount);
|
|
||||||
|
|
||||||
// Transaction.CreditAccount
|
|
||||||
if (model.CreditAccount.HasValue)
|
|
||||||
transaction.CreditAccount = prepareAccount(model.CreditAccount.Value);
|
|
||||||
|
|
||||||
// Transaction.Amount
|
|
||||||
if (model.Amount.HasValue)
|
|
||||||
transaction.Amount = model.Amount.Value;
|
|
||||||
|
|
||||||
// Transaction.IsPending
|
|
||||||
if (model.IsPending.HasValue)
|
|
||||||
transaction.IsPending = model.IsPending.Value;
|
|
||||||
|
|
||||||
this.ValidateTransaction(transaction, ownerId);
|
|
||||||
|
|
||||||
transaction.UpdatedOn = DateTime.UtcNow;
|
|
||||||
|
|
||||||
_context.Transactions.Update(transaction);
|
|
||||||
_context.SaveChanges();
|
|
||||||
|
|
||||||
_logger.LogInformation($"Transaction '{id}' successfully updated.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Delete(int id, int ownerId)
|
|
||||||
{
|
|
||||||
var transaction = getTransaction(id, ownerId);
|
|
||||||
_context.Transactions.Remove(transaction);
|
|
||||||
_context.SaveChanges();
|
|
||||||
}
|
|
||||||
|
|
||||||
// helpers
|
|
||||||
|
|
||||||
private bool ErrorOrFalse(bool error, string errorMessage)
|
|
||||||
{
|
|
||||||
if (error)
|
|
||||||
throw new AppException(errorMessage);
|
|
||||||
|
|
||||||
_logger.LogWarning(errorMessage);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool ValidateTransaction(Transaction transaction, int ownerId, bool errorOnFail = true)
|
|
||||||
{
|
|
||||||
// There has to be at least 1 specified account
|
|
||||||
if (transaction.DebitAccount == null && transaction.CreditAccount == null)
|
|
||||||
return ErrorOrFalse(errorOnFail, "There must be an envelope or account chosen for a transaction.");
|
|
||||||
|
|
||||||
// Accounts cannot be the same
|
|
||||||
if (transaction.DebitAccount != null && transaction.CreditAccount != null &&
|
|
||||||
transaction.DebitAccount.Id == transaction.CreditAccount.Id)
|
|
||||||
return ErrorOrFalse(errorOnFail, "The debit and credit accounts of a transaction cannot be the same.");
|
|
||||||
|
|
||||||
// Transaction Duplication Check - External ID
|
|
||||||
if (!string.IsNullOrWhiteSpace(transaction.ExternalId)
|
|
||||||
&& _context.Transactions
|
|
||||||
.Include(x => x.Owner)
|
|
||||||
.Any(x => x.ExternalId == transaction.ExternalId && x.Owner.Id == ownerId))
|
|
||||||
return ErrorOrFalse(errorOnFail, "Transaction with the external ID '" + transaction.ExternalId + "' already exists");
|
|
||||||
|
|
||||||
// Transaction Duplication Check - All other fields
|
|
||||||
/*if (_context.Transactions.Any(x =>
|
|
||||||
x.Description == transaction.Description
|
|
||||||
&& x.Date == transaction.Date
|
|
||||||
&& x.DebitAccount == transaction.DebitAccount
|
|
||||||
&& x.CreditAccount == transaction.CreditAccount
|
|
||||||
&& x.Amount == transaction.Amount))
|
|
||||||
{
|
|
||||||
return ErrorOrFalse(errorOnFail, "Transaction with the same fields already exists");
|
|
||||||
}*/
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Transaction getTransaction(int id, int ownerId)
|
|
||||||
{
|
|
||||||
var transaction = _context.Transactions
|
|
||||||
.Include(t => t.DebitAccount)
|
|
||||||
.Include(t => t.CreditAccount)
|
|
||||||
.Include(t => t.Owner)
|
|
||||||
.FirstOrDefault(t => t.Id == id && t.Owner.Id == ownerId);
|
|
||||||
|
|
||||||
if (transaction == null)
|
|
||||||
throw new KeyNotFoundException("Transaction not found");
|
|
||||||
|
|
||||||
return transaction;
|
|
||||||
}
|
|
||||||
|
|
||||||
private User getOwner(int ownerId)
|
|
||||||
{
|
|
||||||
User? owner = _context.Users.Find(ownerId);
|
|
||||||
|
|
||||||
if (owner == null)
|
|
||||||
throw new AppException($"Owner with ID of '{ownerId}' could not be found");
|
|
||||||
|
|
||||||
return owner;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,59 +1,30 @@
|
|||||||
namespace AAIntegration.SimmonsBank.API.Services;
|
namespace AAIntegration.SimmonsBank.API.Services;
|
||||||
|
|
||||||
using AutoMapper;
|
|
||||||
using BCrypt.Net;
|
|
||||||
using AAIntegration.SimmonsBank.API.Entities;
|
using AAIntegration.SimmonsBank.API.Entities;
|
||||||
using AAIntegration.SimmonsBank.API.Config;
|
using AAIntegration.SimmonsBank.API.Config;
|
||||||
using AAIntegration.SimmonsBank.API.Models.Users;
|
using AAIntegration.SimmonsBank.API.Models.Users;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Microsoft.EntityFrameworkCore.Internal;
|
|
||||||
using Microsoft.IdentityModel.Tokens;
|
|
||||||
using System.Text;
|
|
||||||
using System.IdentityModel.Tokens.Jwt;
|
|
||||||
using System.Security.Claims;
|
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
|
|
||||||
public interface IUserService
|
public interface IUserService
|
||||||
{
|
{
|
||||||
// New Based way
|
|
||||||
string Create(UserCreateRequest model);
|
string Create(UserCreateRequest model);
|
||||||
void Update(string apiKey, UserUpdateRequest model);
|
void Update(string apiKey, UserUpdateRequest model);
|
||||||
void Delete(string apiKey);
|
void Delete(string apiKey);
|
||||||
Dictionary<string, int> GetAllApiKeys();
|
Dictionary<string, int> GetAllApiKeys();
|
||||||
User GetUser(string ApiKey);
|
User GetUser(string ApiKey);
|
||||||
IEnumerable<User> GetAll();
|
IEnumerable<User> GetAll();
|
||||||
|
|
||||||
/* Other cringe way
|
|
||||||
AuthenticateResponse Authenticate(AuthenticateRequest model);
|
|
||||||
void Register(RegisterRequest model);
|
|
||||||
IEnumerable<User> GetAll();
|
|
||||||
User GetById(int id);
|
|
||||||
void Update(int id, UserUpdateRequest model);
|
|
||||||
void Delete(int id);
|
|
||||||
Dictionary<string, int> GetAllApiKeys();
|
|
||||||
string GetUserApiKey(int id);
|
|
||||||
void InvalidateApiKey(string apiKey);
|
|
||||||
string CreateUserApiKey(int id);
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class UserService : IUserService
|
public class UserService : IUserService
|
||||||
{
|
{
|
||||||
private DataContext _context;
|
private DataContext _context;
|
||||||
private readonly IMapper _mapper;
|
|
||||||
private readonly IOptions<AppSettings> _appSettings;
|
|
||||||
|
|
||||||
public UserService(
|
public UserService(
|
||||||
DataContext context,
|
DataContext context)
|
||||||
IMapper mapper,
|
|
||||||
IOptions<AppSettings> appSettings)
|
|
||||||
{
|
{
|
||||||
_context = context;
|
_context = context;
|
||||||
_mapper = mapper;
|
|
||||||
_appSettings = appSettings;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Create(UserCreateRequest model)
|
public string Create(UserCreateRequest model)
|
||||||
@ -130,7 +101,7 @@ public class UserService : IUserService
|
|||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
private const string _prefix = "CT-";
|
private const string _prefix = "SB-";
|
||||||
private const int _numberOfSecureBytesToGenerate = 32;
|
private const int _numberOfSecureBytesToGenerate = 32;
|
||||||
private const int _lengthOfKey = 32;
|
private const int _lengthOfKey = 32;
|
||||||
|
|
||||||
|
@ -4,6 +4,10 @@ This is an integration for ActiveAllocator.
|
|||||||
|
|
||||||
The type is Transaction Importer, specifically created for interfacing with SimmonsBank's online banking website.
|
The type is Transaction Importer, specifically created for interfacing with SimmonsBank's online banking website.
|
||||||
|
|
||||||
|
## Guides
|
||||||
|
|
||||||
|
[Pass parameters to HTTP GET action](https://code-maze.com/aspnetcore-pass-parameters-to-http-get-action/)
|
||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
|
|
||||||
[Otp.NET library](https://github.com/kspearrin/Otp.NET)
|
[Otp.NET library](https://github.com/kspearrin/Otp.NET)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user