diff --git a/TransactionMobile.Maui.BusinessLogic.Tests/RequestHandlerTests/LoginRequestHandlerTests.cs b/TransactionMobile.Maui.BusinessLogic.Tests/RequestHandlerTests/LoginRequestHandlerTests.cs index 34e11936..5c4fa555 100644 --- a/TransactionMobile.Maui.BusinessLogic.Tests/RequestHandlerTests/LoginRequestHandlerTests.cs +++ b/TransactionMobile.Maui.BusinessLogic.Tests/RequestHandlerTests/LoginRequestHandlerTests.cs @@ -24,9 +24,29 @@ public async Task LoginRequestHandler_Handle_LoginRequest_IsHandled() LoginRequest request = LoginRequest.Create(TestData.UserName,TestData.Password); - String accessToken = await handler.Handle(request, CancellationToken.None); + TokenResponseModel accessToken = await handler.Handle(request, CancellationToken.None); - accessToken.ShouldBe(TestData.AccessToken); + accessToken.AccessToken.ShouldBe(TestData.Token); + accessToken.ExpiryInMinutes.ShouldBe(TestData.TokenExpiryInMinutes); + accessToken.RefreshToken.ShouldBe(TestData.RefreshToken); + } + + [Fact] + public async Task LoginRequestHandler_Handle_RefreshTokenRequest_IsHandled() + { + Mock authenticationService = new Mock(); + authenticationService.Setup(a => a.RefreshAccessToken(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.AccessToken); + Mock configurationService = new Mock(); + configurationService.Setup(c => c.GetConfiguration(It.IsAny(), It.IsAny())).ReturnsAsync(new Configuration()); + LoginRequestHandler handler = new LoginRequestHandler(authenticationService.Object, configurationService.Object); + + RefreshTokenRequest request = RefreshTokenRequest.Create(TestData.RefreshToken); + + TokenResponseModel accessToken = await handler.Handle(request, CancellationToken.None); + + accessToken.AccessToken.ShouldBe(TestData.Token); + accessToken.ExpiryInMinutes.ShouldBe(TestData.TokenExpiryInMinutes); + accessToken.RefreshToken.ShouldBe(TestData.RefreshToken); } [Fact] diff --git a/TransactionMobile.Maui.BusinessLogic.Tests/RequestHandlerTests/MerchantRequestHandlerTests.cs b/TransactionMobile.Maui.BusinessLogic.Tests/RequestHandlerTests/MerchantRequestHandlerTests.cs index 63cd7192..8ce8b043 100644 --- a/TransactionMobile.Maui.BusinessLogic.Tests/RequestHandlerTests/MerchantRequestHandlerTests.cs +++ b/TransactionMobile.Maui.BusinessLogic.Tests/RequestHandlerTests/MerchantRequestHandlerTests.cs @@ -22,7 +22,7 @@ public async Task MerchantRequestHandler_GetContractProductsRequest_Handle_IsHan .ReturnsAsync(TestData.ContractProductList); MerchantRequestHandler handler = new MerchantRequestHandler(merchantService.Object); - GetContractProductsRequest request = GetContractProductsRequest.Create(TestData.AccessToken, + GetContractProductsRequest request = GetContractProductsRequest.Create(TestData.Token, TestData.EstateId, TestData.MerchantId, null); @@ -40,7 +40,7 @@ public async Task MerchantRequestHandler_GetMerchantBalanceRequest_Handle_IsHand .ReturnsAsync(TestData.MerchantBalance); MerchantRequestHandler handler = new MerchantRequestHandler(merchantService.Object); - GetMerchantBalanceRequest request = GetMerchantBalanceRequest.Create(TestData.AccessToken, + GetMerchantBalanceRequest request = GetMerchantBalanceRequest.Create(TestData.Token, TestData.EstateId, TestData.MerchantId); diff --git a/TransactionMobile.Maui.BusinessLogic.Tests/RequestHandlerTests/TransactionRequestHandlerTests.cs b/TransactionMobile.Maui.BusinessLogic.Tests/RequestHandlerTests/TransactionRequestHandlerTests.cs index 3767b2c2..26ef9c0d 100644 --- a/TransactionMobile.Maui.BusinessLogic.Tests/RequestHandlerTests/TransactionRequestHandlerTests.cs +++ b/TransactionMobile.Maui.BusinessLogic.Tests/RequestHandlerTests/TransactionRequestHandlerTests.cs @@ -17,7 +17,7 @@ public class TransactionRequestHandlerTests public async Task TransactionRequestHandler_LogonTransactionRequest_Handle_IsHandled() { Mock transactionService = new Mock(); - transactionService.Setup(t => t.PerformLogon(It.IsAny(), It.IsAny())).ReturnsAsync(true); + transactionService.Setup(t => t.PerformLogon(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.PerformLogonResponseModel); TransactionRequestHandler handler = new TransactionRequestHandler(transactionService.Object); LogonTransactionRequest request = LogonTransactionRequest.Create(TestData.TransactionDateTime, @@ -25,9 +25,9 @@ public async Task TransactionRequestHandler_LogonTransactionRequest_Handle_IsHan TestData.DeviceIdentifier, TestData.ApplicationVersion); - Boolean response = await handler.Handle(request, CancellationToken.None); + PerformLogonResponseModel? response = await handler.Handle(request, CancellationToken.None); - response.ShouldBeTrue(); + response.IsSuccessful.ShouldBeTrue(); } [Fact] diff --git a/TransactionMobile.Maui.BusinessLogic.Tests/RequestTests/RequestTests.cs b/TransactionMobile.Maui.BusinessLogic.Tests/RequestTests/RequestTests.cs index bc0443ad..d9424338 100644 --- a/TransactionMobile.Maui.BusinessLogic.Tests/RequestTests/RequestTests.cs +++ b/TransactionMobile.Maui.BusinessLogic.Tests/RequestTests/RequestTests.cs @@ -15,13 +15,13 @@ public class RequestTests [Fact] public void GetContractProductsRequest_Create_IsCreated() { - GetContractProductsRequest request = GetContractProductsRequest.Create(TestData.AccessToken, + GetContractProductsRequest request = GetContractProductsRequest.Create(TestData.Token, TestData.EstateId, TestData.MerchantId, null); request.ShouldNotBeNull(); - request.AccessToken.ShouldBe(TestData.AccessToken); + request.AccessToken.ShouldBe(TestData.Token); request.EstateId.ShouldBe(TestData.EstateId); request.MerchantId.ShouldBe(TestData.MerchantId); } @@ -29,12 +29,12 @@ public void GetContractProductsRequest_Create_IsCreated() [Fact] public void GetMerchantBalanceRequest_Create_IsCreated() { - GetMerchantBalanceRequest request = GetMerchantBalanceRequest.Create(TestData.AccessToken, + GetMerchantBalanceRequest request = GetMerchantBalanceRequest.Create(TestData.Token, TestData.EstateId, TestData.MerchantId); request.ShouldNotBeNull(); - request.AccessToken.ShouldBe(TestData.AccessToken); + request.AccessToken.ShouldBe(TestData.Token); request.EstateId.ShouldBe(TestData.EstateId); request.MerchantId.ShouldBe(TestData.MerchantId); } @@ -50,6 +50,15 @@ public void LoginRequest_Create_IsCreated() request.Password.ShouldBe(TestData.Password); } + [Fact] + public void RefreshTokenRequest_Create_IsCreated() + { + RefreshTokenRequest request = RefreshTokenRequest.Create(TestData.RefreshToken); + + request.ShouldNotBeNull(); + request.RefreshToken.ShouldBe(TestData.RefreshToken); + } + [Fact] public void LogonTransactionRequest_Create_IsCreated() { diff --git a/TransactionMobile.Maui.BusinessLogic.Tests/TestData.cs b/TransactionMobile.Maui.BusinessLogic.Tests/TestData.cs index c647bd55..29875ef7 100644 --- a/TransactionMobile.Maui.BusinessLogic.Tests/TestData.cs +++ b/TransactionMobile.Maui.BusinessLogic.Tests/TestData.cs @@ -6,11 +6,23 @@ namespace TransactionMobile.Maui.BusinessLogic.Tests { + using Microsoft.Win32.SafeHandles; using Models; public static class TestData { - public static String AccessToken = "Token"; + public static TokenResponseModel AccessToken => + new TokenResponseModel + { + ExpiryInMinutes = TestData.TokenExpiryInMinutes, + AccessToken = TestData.Token, + RefreshToken = TestData.RefreshToken + }; + + public static String Token = "Token"; + + public static String RefreshToken = "RefreshToken"; + public static Int32 TokenExpiryInMinutes = 5; public static Guid EstateId = Guid.Parse("21D339F4-C97F-4C30-A212-11CA01E2D508"); public static Guid MerchantId = Guid.Parse("E8B4B839-434A-43A2-B373-D8813F63F615"); @@ -128,6 +140,16 @@ public static class TestData public static String RecipientEmailAddress = "1@2.com"; public static Decimal MerchantBalance = 199.99m; + + public static PerformLogonResponseModel PerformLogonResponseModel => + new PerformLogonResponseModel + { + EstateId = TestData.EstateId, + MerchantId = TestData.MerchantId, + IsSuccessful = true, + RequireApplicationUpdate = false, + ResponseMessage = "SUCCESS" + }; } } \ No newline at end of file diff --git a/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/LoginPageViewModelTests.cs b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/LoginPageViewModelTests.cs index cb6bc877..85328ced 100644 --- a/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/LoginPageViewModelTests.cs +++ b/TransactionMobile.Maui.BusinessLogic.Tests/ViewModelTests/LoginPageViewModelTests.cs @@ -1,8 +1,11 @@ namespace TransactionMobile.Maui.BusinessLogic.Tests.ViewModelTests; +using System; using System.Threading; using Maui.UIServices; using MediatR; +using Microsoft.Extensions.Caching.Memory; +using Models; using Moq; using Requests; using UIServices; @@ -16,9 +19,19 @@ public void LoginPageViewModel_LoginCommand_Execute_IsExecuted() { Mock mediator = new Mock(); Mock navigationService = new Mock(); - LoginPageViewModel viewModel = new LoginPageViewModel(mediator.Object, navigationService.Object); + MemoryCache userDetailsCache = new MemoryCache(new MemoryCacheOptions()); + IMemoryCache configurationCache = new MemoryCache(new MemoryCacheOptions()); + Mock deviceService = new Mock(); + Mock applicationInfoService = new Mock(); + LoginPageViewModel viewModel = new LoginPageViewModel(mediator.Object, navigationService.Object,userDetailsCache,configurationCache, + deviceService.Object,applicationInfoService.Object); + mediator.Setup(m => m.Send(It.IsAny(), It.IsAny())).ReturnsAsync(new Configuration()); + mediator.Setup(m => m.Send(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.AccessToken); + mediator.Setup(m => m.Send(It.IsAny(), It.IsAny())).ReturnsAsync(TestData.PerformLogonResponseModel); + viewModel.LoginCommand.Execute(null); + mediator.Verify(x => x.Send(It.IsAny(), It.IsAny()), Times.Once); mediator.Verify(x => x.Send(It.IsAny(), It.IsAny()), Times.Once); mediator.Verify(x => x.Send(It.IsAny(), It.IsAny()), Times.Once); diff --git a/TransactionMobile.Maui.BusinessLogic/Models/PerformLogonResponseModel.cs b/TransactionMobile.Maui.BusinessLogic/Models/PerformLogonResponseModel.cs new file mode 100644 index 00000000..05e3878f --- /dev/null +++ b/TransactionMobile.Maui.BusinessLogic/Models/PerformLogonResponseModel.cs @@ -0,0 +1,18 @@ +namespace TransactionMobile.Maui.BusinessLogic.Models; + +public class PerformLogonResponseModel +{ + #region Properties + + public Boolean IsSuccessful { get; set; } + + public String ResponseMessage { get; set; } + + public Guid EstateId { get; set; } + + public Guid MerchantId { get; set; } + + public Boolean RequireApplicationUpdate { get; set; } + + #endregion +} \ No newline at end of file diff --git a/TransactionMobile.Maui.BusinessLogic/Models/TokenResponseModel.cs b/TransactionMobile.Maui.BusinessLogic/Models/TokenResponseModel.cs new file mode 100644 index 00000000..8a406357 --- /dev/null +++ b/TransactionMobile.Maui.BusinessLogic/Models/TokenResponseModel.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace TransactionMobile.Maui.BusinessLogic.Models; + +public class TokenResponseModel +{ + public String AccessToken { get; set; } + public String RefreshToken { get; set; } + public Int32 ExpiryInMinutes { get; set; } +} \ No newline at end of file diff --git a/TransactionMobile.Maui.BusinessLogic/RequestHandlers/LoginRequestHandler.cs b/TransactionMobile.Maui.BusinessLogic/RequestHandlers/LoginRequestHandler.cs index 0867fe45..9d18137d 100644 --- a/TransactionMobile.Maui.BusinessLogic/RequestHandlers/LoginRequestHandler.cs +++ b/TransactionMobile.Maui.BusinessLogic/RequestHandlers/LoginRequestHandler.cs @@ -5,7 +5,8 @@ using Requests; using Services; - public class LoginRequestHandler : IRequestHandler, + public class LoginRequestHandler : IRequestHandler, + IRequestHandler, IRequestHandler { private readonly IConfigurationService ConfigurationService; @@ -29,10 +30,10 @@ public LoginRequestHandler(IAuthenticationService authenticationService, #region Methods - public async Task Handle(LoginRequest request, - CancellationToken cancellationToken) + public async Task Handle(LoginRequest request, + CancellationToken cancellationToken) { - var token = await this.AuthenticationService.GetToken(request.UserName, request.Password, cancellationToken); + TokenResponseModel token = await this.AuthenticationService.GetToken(request.UserName, request.Password, cancellationToken); return token; } @@ -44,5 +45,13 @@ public async Task Handle(GetConfigurationRequest request, } #endregion + + public async Task Handle(RefreshTokenRequest request, + CancellationToken cancellationToken) + { + TokenResponseModel token = await this.AuthenticationService.RefreshAccessToken(request.RefreshToken, cancellationToken); + + return token; + } } } \ No newline at end of file diff --git a/TransactionMobile.Maui.BusinessLogic/RequestHandlers/TransactionRequestHandler.cs b/TransactionMobile.Maui.BusinessLogic/RequestHandlers/TransactionRequestHandler.cs index b716e4ce..904e99f6 100644 --- a/TransactionMobile.Maui.BusinessLogic/RequestHandlers/TransactionRequestHandler.cs +++ b/TransactionMobile.Maui.BusinessLogic/RequestHandlers/TransactionRequestHandler.cs @@ -6,7 +6,7 @@ using Services; public class TransactionRequestHandler : IRequestHandler, - IRequestHandler, + IRequestHandler, IRequestHandler { #region Fields @@ -49,8 +49,8 @@ public async Task Handle(PerformMobileTopupRequest request, return result; } - public async Task Handle(LogonTransactionRequest request, - CancellationToken cancellationToken) + public async Task Handle(LogonTransactionRequest request, + CancellationToken cancellationToken) { // TODO: Factory PerformLogonRequestModel model = new PerformLogonRequestModel @@ -61,7 +61,7 @@ public async Task Handle(LogonTransactionRequest request, TransactionNumber = request.TransactionNumber }; - Boolean result = await this.TransactionService.PerformLogon(model, cancellationToken); + PerformLogonResponseModel result = await this.TransactionService.PerformLogon(model, cancellationToken); return result; } diff --git a/TransactionMobile.Maui.BusinessLogic/Requests/LoginRequest.cs b/TransactionMobile.Maui.BusinessLogic/Requests/LoginRequest.cs index 1e8a99b1..cb1bd1bb 100644 --- a/TransactionMobile.Maui.BusinessLogic/Requests/LoginRequest.cs +++ b/TransactionMobile.Maui.BusinessLogic/Requests/LoginRequest.cs @@ -1,8 +1,9 @@ namespace TransactionMobile.Maui.BusinessLogic.Requests { using MediatR; + using Models; - public class LoginRequest : IRequest + public class LoginRequest : IRequest { #region Constructors diff --git a/TransactionMobile.Maui.BusinessLogic/Requests/LogonTransactionRequest.cs b/TransactionMobile.Maui.BusinessLogic/Requests/LogonTransactionRequest.cs index 963c015b..60136a21 100644 --- a/TransactionMobile.Maui.BusinessLogic/Requests/LogonTransactionRequest.cs +++ b/TransactionMobile.Maui.BusinessLogic/Requests/LogonTransactionRequest.cs @@ -1,8 +1,9 @@ namespace TransactionMobile.Maui.BusinessLogic.Requests; using MediatR; +using Models; -public class LogonTransactionRequest : IRequest +public class LogonTransactionRequest : IRequest { public String DeviceIdentifier { get; private set; } public DateTime TransactionDateTime { get; private set; } diff --git a/TransactionMobile.Maui.BusinessLogic/Requests/RefreshTokenRequest.cs b/TransactionMobile.Maui.BusinessLogic/Requests/RefreshTokenRequest.cs new file mode 100644 index 00000000..10c71732 --- /dev/null +++ b/TransactionMobile.Maui.BusinessLogic/Requests/RefreshTokenRequest.cs @@ -0,0 +1,31 @@ +namespace TransactionMobile.Maui.BusinessLogic.Requests; + +using MediatR; +using Models; + +public class RefreshTokenRequest : IRequest +{ + #region Constructors + + private RefreshTokenRequest(String refreshToken) + { + this.RefreshToken = refreshToken; + } + + #endregion + + #region Properties + + public String RefreshToken { get; } + + #endregion + + #region Methods + + public static RefreshTokenRequest Create(String refreshToken) + { + return new RefreshTokenRequest(refreshToken); + } + + #endregion +} \ No newline at end of file diff --git a/TransactionMobile.Maui.BusinessLogic/Services/DummyServices/DummyAuthenticationService.cs b/TransactionMobile.Maui.BusinessLogic/Services/DummyServices/DummyAuthenticationService.cs index b362642b..5ffe95b4 100644 --- a/TransactionMobile.Maui.BusinessLogic/Services/DummyServices/DummyAuthenticationService.cs +++ b/TransactionMobile.Maui.BusinessLogic/Services/DummyServices/DummyAuthenticationService.cs @@ -1,14 +1,32 @@ namespace TransactionMobile.Maui.BusinessLogic.Services.DummyServices; +using Models; + public class DummyAuthenticationService : IAuthenticationService { #region Methods - public async Task GetToken(String username, - String password, - CancellationToken cancellationToken) + public async Task GetToken(String username, + String password, + CancellationToken cancellationToken) + { + return new TokenResponseModel + { + AccessToken = "Token", + RefreshToken = "RefreshToken", + ExpiryInMinutes = 1 + }; + } + + public async Task RefreshAccessToken(String refreshToken, + CancellationToken cancellationToken) { - return "MyToken"; + return new TokenResponseModel + { + AccessToken = "Token", + RefreshToken = "RefreshToken", + ExpiryInMinutes = 1 + }; } #endregion diff --git a/TransactionMobile.Maui.BusinessLogic/Services/DummyServices/DummyTransactionService.cs b/TransactionMobile.Maui.BusinessLogic/Services/DummyServices/DummyTransactionService.cs index aa7865a5..710d4b63 100644 --- a/TransactionMobile.Maui.BusinessLogic/Services/DummyServices/DummyTransactionService.cs +++ b/TransactionMobile.Maui.BusinessLogic/Services/DummyServices/DummyTransactionService.cs @@ -6,10 +6,17 @@ public class DummyTransactionService : ITransactionService { #region Methods - public async Task PerformLogon(PerformLogonRequestModel model, - CancellationToken cancellationToken) + public async Task PerformLogon(PerformLogonRequestModel model, + CancellationToken cancellationToken) { - return true; + return new PerformLogonResponseModel + { + EstateId = Guid.Parse("D7E52254-E0BE-436A-9A34-CC291DA0D66A"), + MerchantId = Guid.Parse("DD034A3B-D8EE-45A4-A29F-8774751CEE76"), + IsSuccessful = true, + ResponseMessage = "SUCCESS", + RequireApplicationUpdate = false + }; } public async Task PerformMobileTopup(PerformMobileTopupRequestModel model, diff --git a/TransactionMobile.Maui.BusinessLogic/Services/IAuthenticationService.cs b/TransactionMobile.Maui.BusinessLogic/Services/IAuthenticationService.cs index 9617d4ff..05e229af 100644 --- a/TransactionMobile.Maui.BusinessLogic/Services/IAuthenticationService.cs +++ b/TransactionMobile.Maui.BusinessLogic/Services/IAuthenticationService.cs @@ -1,12 +1,17 @@ namespace TransactionMobile.Maui.BusinessLogic.Services { + using Models; + public interface IAuthenticationService { #region Methods - Task GetToken(String username, - String password, - CancellationToken cancellationToken); + Task GetToken(String username, + String password, + CancellationToken cancellationToken); + + Task RefreshAccessToken(String refreshToken, + CancellationToken cancellationToken); #endregion } diff --git a/TransactionMobile.Maui.BusinessLogic/Services/ITransactionService.cs b/TransactionMobile.Maui.BusinessLogic/Services/ITransactionService.cs index 163c80a1..b9df649b 100644 --- a/TransactionMobile.Maui.BusinessLogic/Services/ITransactionService.cs +++ b/TransactionMobile.Maui.BusinessLogic/Services/ITransactionService.cs @@ -6,7 +6,7 @@ public interface ITransactionService { #region Methods - Task PerformLogon(PerformLogonRequestModel model, CancellationToken cancellationToken); + Task PerformLogon(PerformLogonRequestModel model, CancellationToken cancellationToken); Task PerformMobileTopup(PerformMobileTopupRequestModel model, CancellationToken cancellationToken); diff --git a/TransactionMobile.Maui.BusinessLogic/TransactionMobile.Maui.BusinessLogic.csproj b/TransactionMobile.Maui.BusinessLogic/TransactionMobile.Maui.BusinessLogic.csproj index 2ea0cced..cfdcafbb 100644 --- a/TransactionMobile.Maui.BusinessLogic/TransactionMobile.Maui.BusinessLogic.csproj +++ b/TransactionMobile.Maui.BusinessLogic/TransactionMobile.Maui.BusinessLogic.csproj @@ -16,6 +16,7 @@ + diff --git a/TransactionMobile.Maui.BusinessLogic/ViewModels/LoginPageViewModel.cs b/TransactionMobile.Maui.BusinessLogic/ViewModels/LoginPageViewModel.cs index 5e2c32a4..bc19fc5a 100644 --- a/TransactionMobile.Maui.BusinessLogic/ViewModels/LoginPageViewModel.cs +++ b/TransactionMobile.Maui.BusinessLogic/ViewModels/LoginPageViewModel.cs @@ -3,6 +3,8 @@ using System.Windows.Input; using Maui.UIServices; using MediatR; + using Microsoft.Extensions.Caching.Memory; + using Microsoft.Extensions.Primitives; using Models; using MvvmHelpers; using MvvmHelpers.Commands; @@ -13,11 +15,28 @@ public class LoginPageViewModel : BaseViewModel { private readonly INavigationService NavigationService; + private readonly IMemoryCache UserDetailsCache; + + private readonly IMemoryCache ConfigurationCache; + + private readonly IDeviceService DeviceService; + + private readonly IApplicationInfoService ApplicationInfoService; + + private String userName; + + private String password; + #region Constructors - public LoginPageViewModel(IMediator mediator, INavigationService navigationService) + public LoginPageViewModel(IMediator mediator, INavigationService navigationService, IMemoryCache userDetailsCache, IMemoryCache configurationCache, + IDeviceService deviceService,IApplicationInfoService applicationInfoService) { this.NavigationService = navigationService; + this.UserDetailsCache = userDetailsCache; + this.ConfigurationCache = configurationCache; + this.DeviceService = deviceService; + this.ApplicationInfoService = applicationInfoService; this.LoginCommand = new AsyncCommand(this.LoginCommandExecute); this.Mediator = mediator; } @@ -30,6 +49,18 @@ public LoginPageViewModel(IMediator mediator, INavigationService navigationServi public IMediator Mediator { get; } + public String UserName + { + get => this.userName; + set => this.SetProperty(ref this.userName, value); + } + + public String Password + { + get => this.password; + set => this.SetProperty(ref this.password, value); + } + #endregion #region Methods @@ -37,39 +68,87 @@ public LoginPageViewModel(IMediator mediator, INavigationService navigationServi private async Task LoginCommandExecute() { // TODO: this method needs refactored - GetConfigurationRequest getConfigurationRequest = GetConfigurationRequest.Create(""); + + GetConfigurationRequest getConfigurationRequest = GetConfigurationRequest.Create(this.DeviceService.GetIdentifier()); Configuration configuration = await this.Mediator.Send(getConfigurationRequest); - // TODO: Cache the config object somewhere + // Cache the config object + this.ConfigurationCache.Set("Configuration", configuration); - LoginRequest loginRequest = LoginRequest.Create("", ""); - String token = await this.Mediator.Send(loginRequest); + LoginRequest loginRequest = LoginRequest.Create(this.UserName, this.Password); + TokenResponseModel token = await this.Mediator.Send(loginRequest); //if (token == null) //{ - // // TODO: Some kind of error handling + // TODO: Some kind of error handling //} + // Cache the token + this.CacheAccessToken(token); - // TODO: Logon Transaction - LogonTransactionRequest logonTransactionRequest = LogonTransactionRequest.Create(DateTime.Now, "1", "", ""); - Boolean logonSuccessful = await this.Mediator.Send(logonTransactionRequest); + // Logon Transaction + LogonTransactionRequest logonTransactionRequest = LogonTransactionRequest.Create(DateTime.Now, "1", + this.DeviceService.GetIdentifier(), + this.ApplicationInfoService.VersionString); + PerformLogonResponseModel logonResponse = await this.Mediator.Send(logonTransactionRequest); - // TODO: get these values off the logon response (maybe make response a tuple) - var estateId = Guid.Parse("56CEE156-6815-4562-A96E-9389C16FA79B"); - var merchantId = Guid.Parse("E746EACB-4E73-4E78-B732-53B9C65E5BDA"); + if (logonResponse.IsSuccessful == false) + { + // TODO: Throw an error here + } - // TODO: Get Contracts & Balance ?? - GetContractProductsRequest getContractProductsRequest = GetContractProductsRequest.Create("", estateId, merchantId, null); + // Set the user information + this.UserDetailsCache.Set("EstateId", logonResponse.EstateId); + this.UserDetailsCache.Set("Merchant", logonResponse.MerchantId); + // Get Contracts // TODO: Cache the result, but will add this to a timer call to keep up to date... + GetContractProductsRequest getContractProductsRequest = GetContractProductsRequest.Create(token.AccessToken, + logonResponse.EstateId, + logonResponse.MerchantId, null); List products = await this.Mediator.Send(getContractProductsRequest); + // Get the merchant balance // TODO: Cache the result, but will add this to a timer call to keep up to date... - GetMerchantBalanceRequest getMerchantBalanceRequest = GetMerchantBalanceRequest.Create("", estateId, merchantId); - var merchantBalance = await this.Mediator.Send(getMerchantBalanceRequest); + GetMerchantBalanceRequest getMerchantBalanceRequest = GetMerchantBalanceRequest.Create(token.AccessToken, + logonResponse.EstateId, + logonResponse.MerchantId); + Decimal merchantBalance = await this.Mediator.Send(getMerchantBalanceRequest); - // TODO: Cache the token as will be needed later await this.NavigationService.GoToHome(); } + + private async void AccessTokenExpired(Object key, + Object value, + EvictionReason reason, + Object state) + { + if (reason == EvictionReason.Expired || reason == EvictionReason.TokenExpired) + { + // access token has expired need to make another call to get new one + TokenResponseModel token = value as TokenResponseModel; + + RefreshTokenRequest request = RefreshTokenRequest.Create(token.RefreshToken); + TokenResponseModel newToken = await this.Mediator.Send(request, CancellationToken.None); + + this.CacheAccessToken(newToken); + } + } + + private void CacheAccessToken(TokenResponseModel token) + { + DateTime expirationTime = DateTime.Now.AddMinutes(token.ExpiryInMinutes).AddSeconds(-30); + CancellationChangeToken expirationToken = new CancellationChangeToken(new CancellationTokenSource(TimeSpan.FromMinutes(token.ExpiryInMinutes).Add(TimeSpan.FromSeconds(-30))).Token); + MemoryCacheEntryOptions cacheEntryOptions = new MemoryCacheEntryOptions() + // Pin to cache. + .SetPriority(CacheItemPriority.NeverRemove) + // Set the actual expiration time + .SetAbsoluteExpiration(expirationTime) + // Force eviction to run + .AddExpirationToken(expirationToken) + // Add eviction callback + .RegisterPostEvictionCallback(callback:this.AccessTokenExpired); + + this.UserDetailsCache.Set("AccessToken", token, cacheEntryOptions); + } #endregion } diff --git a/TransactionMobile.Maui/App.xaml.cs b/TransactionMobile.Maui/App.xaml.cs index 17fbe1b9..1e80caa2 100644 --- a/TransactionMobile.Maui/App.xaml.cs +++ b/TransactionMobile.Maui/App.xaml.cs @@ -7,19 +7,9 @@ namespace TransactionMobile.Maui; public partial class App : Application { - /// - /// The estate identifier - /// - public static Guid EstateId; - - /// - /// The merchant identifier - /// - public static Guid MerchantId; - public App() - { - InitializeComponent(); + { + InitializeComponent(); MainPage = new AppShell(); diff --git a/TransactionMobile.Maui/Extensions/MauiAppBuilderExtensions.cs b/TransactionMobile.Maui/Extensions/MauiAppBuilderExtensions.cs index dcc53251..88ff41a1 100644 --- a/TransactionMobile.Maui/Extensions/MauiAppBuilderExtensions.cs +++ b/TransactionMobile.Maui/Extensions/MauiAppBuilderExtensions.cs @@ -38,13 +38,14 @@ public static MauiAppBuilder ConfigureRequestHandlers(this MauiAppBuilder builde { builder.Services.AddSingleton(); builder.Services.AddSingleton, LoginRequestHandler>(); - builder.Services.AddSingleton, LoginRequestHandler>(); + builder.Services.AddSingleton, LoginRequestHandler>(); + builder.Services.AddSingleton, LoginRequestHandler>(); builder.Services.AddSingleton>, MerchantRequestHandler>(); builder.Services.AddSingleton, MerchantRequestHandler>(); builder.Services.AddSingleton, TransactionRequestHandler>(); - builder.Services.AddSingleton, TransactionRequestHandler>(); + builder.Services.AddSingleton, TransactionRequestHandler>(); builder.Services.AddSingleton, TransactionRequestHandler>(); builder.Services.AddSingleton(ctx => { return t => ctx.GetService(t); }); diff --git a/TransactionMobile.Maui/MauiProgram.cs b/TransactionMobile.Maui/MauiProgram.cs index a8e742c9..fe0ced7c 100644 --- a/TransactionMobile.Maui/MauiProgram.cs +++ b/TransactionMobile.Maui/MauiProgram.cs @@ -17,7 +17,8 @@ public static MauiApp CreateMauiApp() { fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular"); }) - .Services.AddTransient(); + .Services.AddTransient() + .AddMemoryCache(); Container = builder.Build(); return Container; diff --git a/TransactionMobile.Maui/Pages/LoginPage.xaml b/TransactionMobile.Maui/Pages/LoginPage.xaml index b7d82c2f..ee1e73ac 100644 --- a/TransactionMobile.Maui/Pages/LoginPage.xaml +++ b/TransactionMobile.Maui/Pages/LoginPage.xaml @@ -22,13 +22,15 @@ + Style="{DynamicResource UserNameEntryStyle}" + Text="{Binding UserName}"> + Style="{DynamicResource PasswordEntryStyle}" + Text="{Binding Password}">