mirror of
https://github.com/DrMboga/WotBlitzStatisticsBlazor.git
synced 2026-10-23 19:30:06 +02:00
feat: prolong token UI logic implemented
This commit is contained in:
1 parent
6390c454d0
commit
7d1d1b5ad6
9 files changed
+223
-13
No files matched your search
@@ -1,22 +1,30 @@
|
|||||||
using System.Threading;
|
using System;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
|
using Moq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using WotBlitzStatisticsPro.Blazor.GraphQl;
|
||||||
using WotBlitzStatisticsPro.Blazor.Messages;
|
using WotBlitzStatisticsPro.Blazor.Messages;
|
||||||
|
using WotBlitzStatisticsPro.Blazor.Model;
|
||||||
using WotBlitzStatisticsPro.Blazor.Services;
|
using WotBlitzStatisticsPro.Blazor.Services;
|
||||||
|
|
||||||
namespace WotBlitzStatisticsPro.Blazor.Tests.Services
|
namespace WotBlitzStatisticsPro.Blazor.Tests.Services
|
||||||
{
|
{
|
||||||
public class NavigatorInterceptorTests
|
public class NavigatorInterceptorTests: TestContextBase
|
||||||
{
|
{
|
||||||
private NavigationMessagesInterceptor _service;
|
private NavigationMessagesInterceptor _service;
|
||||||
private MockNavigationManager _navigationManager;
|
private MockNavigationManager _navigationManager;
|
||||||
|
private Mock<ILocalStorageService> _localStorageMock;
|
||||||
|
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void Setup()
|
public void Setup()
|
||||||
{
|
{
|
||||||
|
_localStorageMock = new();
|
||||||
|
|
||||||
_navigationManager = new MockNavigationManager();
|
_navigationManager = new MockNavigationManager();
|
||||||
_service = new NavigationMessagesInterceptor(_navigationManager);
|
_service = new NavigationMessagesInterceptor(_navigationManager, _localStorageMock.Object, MediatorMock.Object);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,5 +38,90 @@ namespace WotBlitzStatisticsPro.Blazor.Tests.Services
|
|||||||
|
|
||||||
_navigationManager.Uri.Should().Be($"{_navigationManager.BaseUri}player/{accountId}");
|
_navigationManager.Uri.Should().Be($"{_navigationManager.BaseUri}player/{accountId}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task ShouldCallProlongWargamingAccessTokenMessageIfFiveDaysBeforeTokenExpiration()
|
||||||
|
{
|
||||||
|
int accountId = 42;
|
||||||
|
string oldToken = "OldTocken";
|
||||||
|
long expiration = Convert.ToInt64((DateTime.Now.AddDays(4) - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds);
|
||||||
|
_localStorageMock.Setup(s => s.GetItemAsync<LoginInfo>(Constants.LoginInfoLocalStorageKey))
|
||||||
|
.ReturnsAsync(new LoginInfo { AccountId = accountId, AccessToken = oldToken, ExpiresAt = expiration});
|
||||||
|
|
||||||
|
var message = new OpenPlayerInfoMessage(accountId, true);
|
||||||
|
|
||||||
|
await _service.Handle(message, CancellationToken.None);
|
||||||
|
|
||||||
|
MediatorMock.Verify(m => m.Publish(
|
||||||
|
It.IsAny<ProlongWargamingAccessToken>(),
|
||||||
|
It.IsAny<CancellationToken>()), Times.Once);
|
||||||
|
MediatorMock.Verify(m => m.Publish(
|
||||||
|
It.IsAny<RedirectToWgLoginMessage>(),
|
||||||
|
It.IsAny<CancellationToken>()), Times.Never);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task ShouldNotCallProlongWargamingAccessTokenMessageIfMoreThenFiveDaysBeforeTokenExpiration()
|
||||||
|
{
|
||||||
|
int accountId = 42;
|
||||||
|
string oldToken = "OldTocken";
|
||||||
|
long expiration = Convert.ToInt64((DateTime.Now.AddDays(6) - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds);
|
||||||
|
_localStorageMock.Setup(s => s.GetItemAsync<LoginInfo>(Constants.LoginInfoLocalStorageKey))
|
||||||
|
.ReturnsAsync(new LoginInfo { AccountId = accountId, AccessToken = oldToken, ExpiresAt = expiration });
|
||||||
|
|
||||||
|
var message = new OpenPlayerInfoMessage(accountId, true);
|
||||||
|
|
||||||
|
await _service.Handle(message, CancellationToken.None);
|
||||||
|
|
||||||
|
MediatorMock.Verify(m => m.Publish(
|
||||||
|
It.IsAny<ProlongWargamingAccessToken>(),
|
||||||
|
It.IsAny<CancellationToken>()), Times.Never);
|
||||||
|
|
||||||
|
MediatorMock.Verify(m => m.Publish(
|
||||||
|
It.IsAny<RedirectToWgLoginMessage>(),
|
||||||
|
It.IsAny<CancellationToken>()), Times.Never);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task ShouldNotCallProlongWargamingAccessTokenMessageIfPlayerNotLoffedIn()
|
||||||
|
{
|
||||||
|
int accountId = 42;
|
||||||
|
var message = new OpenPlayerInfoMessage(accountId, false);
|
||||||
|
|
||||||
|
await _service.Handle(message, CancellationToken.None);
|
||||||
|
|
||||||
|
MediatorMock.Verify(m => m.Publish(
|
||||||
|
It.IsAny<RedirectToWgLoginMessage>(),
|
||||||
|
It.IsAny<CancellationToken>()), Times.Never);
|
||||||
|
|
||||||
|
MediatorMock.Verify(m => m.Publish(
|
||||||
|
It.IsAny<ProlongWargamingAccessToken>(),
|
||||||
|
It.IsAny<CancellationToken>()), Times.Never);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task ShouldCallRedirectToWgLoginMessageIfTokenIsExpired()
|
||||||
|
{
|
||||||
|
int accountId = 42;
|
||||||
|
string oldToken = "OldTocken";
|
||||||
|
var realm = RealmType.Na;
|
||||||
|
long expiration = Convert.ToInt64((DateTime.Now.AddDays(-1) - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds);
|
||||||
|
_localStorageMock.Setup(s => s.GetItemAsync<LoginInfo>(Constants.LoginInfoLocalStorageKey))
|
||||||
|
.ReturnsAsync(new LoginInfo { AccountId = accountId, AccessToken = oldToken, ExpiresAt = expiration, Realm = realm });
|
||||||
|
|
||||||
|
var message = new OpenPlayerInfoMessage(accountId, true);
|
||||||
|
|
||||||
|
await _service.Handle(message, CancellationToken.None);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
MediatorMock.Verify(m => m.Publish(
|
||||||
|
It.Is<RedirectToWgLoginMessage>(m => m.Realm == realm),
|
||||||
|
It.IsAny<CancellationToken>()), Times.Once);
|
||||||
|
|
||||||
|
MediatorMock.Verify(m => m.Publish(
|
||||||
|
It.IsAny<ProlongWargamingAccessToken>(),
|
||||||
|
It.IsAny<CancellationToken>()), Times.Never);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -96,5 +96,30 @@ namespace WotBlitzStatisticsPro.Blazor.Tests.Services
|
|||||||
It.Is<OpenPlayerInfoMessage>(m => m.AccountId == accountId && m.IsLoggedIn),
|
It.Is<OpenPlayerInfoMessage>(m => m.AccountId == accountId && m.IsLoggedIn),
|
||||||
It.IsAny<CancellationToken>()), Times.Once);
|
It.IsAny<CancellationToken>()), Times.Once);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task ShouldCallAppropriateBackendMethodAndSaveNewTokenToLocalStorageWhenProlongWargamingAccessTokenMessageInvoked()
|
||||||
|
{
|
||||||
|
// Setup
|
||||||
|
int accountId = 42;
|
||||||
|
var realm = RealmType.Na;
|
||||||
|
string oldToken = "OldToken";
|
||||||
|
string newToken = "NewToken";
|
||||||
|
long newEvpiration = 129876543;
|
||||||
|
|
||||||
|
_localStorageMock.Setup(s => s.GetItemAsync<LoginInfo>(Constants.LoginInfoLocalStorageKey))
|
||||||
|
.ReturnsAsync(new LoginInfo { AccountId = accountId, AccessToken = oldToken, ExpiresAt = 12345678, Realm = realm });
|
||||||
|
_graphQlBackendMock.Setup(s => s.ProlongToken(accountId, oldToken, realm)).ReturnsAsync(new LoginInfo { AccountId = accountId, AccessToken = newToken, ExpiresAt = newEvpiration, Realm = realm });
|
||||||
|
|
||||||
|
// Avtion
|
||||||
|
await _service.Handle(new ProlongWargamingAccessToken(), CancellationToken.None);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
_graphQlBackendMock.Verify(s => s.ProlongToken(accountId, oldToken, realm), Times.Once);
|
||||||
|
_localStorageMock.Verify(s => s.SetItemAsync<LoginInfo>(
|
||||||
|
Constants.LoginInfoLocalStorageKey,
|
||||||
|
It.Is<LoginInfo>(l => l.Realm == realm && l.AccountId == accountId && l.AccessToken == newToken && l.ExpiresAt == newEvpiration))
|
||||||
|
, Times.Once);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
using MediatR;
|
||||||
|
|
||||||
|
namespace WotBlitzStatisticsPro.Blazor.Messages
|
||||||
|
{
|
||||||
|
public class ProlongWargamingAccessToken: INotification
|
||||||
|
{
|
||||||
|
public ProlongWargamingAccessToken()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ using System.Threading.Tasks;
|
|||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
using Microsoft.Extensions.Localization;
|
using Microsoft.Extensions.Localization;
|
||||||
using WotBlitzStatisticsPro.Blazor.GraphQl;
|
using WotBlitzStatisticsPro.Blazor.GraphQl;
|
||||||
|
using WotBlitzStatisticsPro.Blazor.Model;
|
||||||
using WotBlitzStatisticsPro.Blazor.Services;
|
using WotBlitzStatisticsPro.Blazor.Services;
|
||||||
|
|
||||||
namespace WotBlitzStatisticsPro.Blazor.Pages
|
namespace WotBlitzStatisticsPro.Blazor.Pages
|
||||||
@@ -30,13 +31,17 @@ namespace WotBlitzStatisticsPro.Blazor.Pages
|
|||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
|
var loggedInInfo = await LocalStorageService.GetItemAsync<LoginInfo>(Constants.LoginInfoLocalStorageKey);
|
||||||
var settings = await LocalStorageService.ReadSettings();
|
var settings = await LocalStorageService.ReadSettings();
|
||||||
CurrentRealmType = settings.RealmType;
|
CurrentRealmType = settings.RealmType;
|
||||||
|
if(loggedInInfo == null)
|
||||||
(AccountInfo, AchievementsBySection) = await GraphQlBackendService.GetPlayerInfo(AccountId, CurrentRealmType);
|
{
|
||||||
|
(AccountInfo, AchievementsBySection) = await GraphQlBackendService.GetPlayerInfo(AccountId, CurrentRealmType);
|
||||||
await base.OnInitializedAsync();
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
(AccountInfo, AchievementsBySection) = await GraphQlBackendService.CollectPlayerInfo(AccountId, CurrentRealmType, loggedInInfo.AccessToken);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,7 @@ using System.Threading.Tasks;
|
|||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using StrawberryShake;
|
using StrawberryShake;
|
||||||
using WotBlitzStatisticsPro.Blazor.GraphQl;
|
using WotBlitzStatisticsPro.Blazor.GraphQl;
|
||||||
|
using WotBlitzStatisticsPro.Blazor.Model;
|
||||||
|
|
||||||
namespace WotBlitzStatisticsPro.Blazor.Services
|
namespace WotBlitzStatisticsPro.Blazor.Services
|
||||||
{
|
{
|
||||||
@@ -99,5 +100,16 @@ namespace WotBlitzStatisticsPro.Blazor.Services
|
|||||||
_notificationsService.ReportError("Backend error",string.Join(";", messages));
|
_notificationsService.ReportError("Backend error",string.Join(";", messages));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task<LoginInfo> ProlongToken(long accountId, string oldToken, RealmType realmType)
|
||||||
|
{
|
||||||
|
throw new System.NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<(IAccount accountInfo, IReadOnlyList<ISection> achievementsBySection)> CollectPlayerInfo(long accountId, RealmType realmType, string accessToken)
|
||||||
|
{
|
||||||
|
// ToDo: Call mutation with accessToken in the context
|
||||||
|
throw new System.NotImplementedException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using WotBlitzStatisticsPro.Blazor.GraphQl;
|
using WotBlitzStatisticsPro.Blazor.GraphQl;
|
||||||
|
using WotBlitzStatisticsPro.Blazor.Model;
|
||||||
|
|
||||||
namespace WotBlitzStatisticsPro.Blazor.Services
|
namespace WotBlitzStatisticsPro.Blazor.Services
|
||||||
{
|
{
|
||||||
@@ -18,6 +19,16 @@ namespace WotBlitzStatisticsPro.Blazor.Services
|
|||||||
long accountId,
|
long accountId,
|
||||||
RealmType realmType);
|
RealmType realmType);
|
||||||
|
|
||||||
|
Task<(IAccount accountInfo, IReadOnlyList<ISection> achievementsBySection)> CollectPlayerInfo(
|
||||||
|
long accountId,
|
||||||
|
RealmType realmType,
|
||||||
|
string accessToken);
|
||||||
|
|
||||||
Task<string> GetWgLoginUrl(RealmType realmType);
|
Task<string> GetWgLoginUrl(RealmType realmType);
|
||||||
|
|
||||||
|
Task<LoginInfo> ProlongToken(
|
||||||
|
long accountId,
|
||||||
|
string oldToken,
|
||||||
|
RealmType realmType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,7 @@ using System.Threading.Tasks;
|
|||||||
using Bogus;
|
using Bogus;
|
||||||
using Bogus.Extensions;
|
using Bogus.Extensions;
|
||||||
using WotBlitzStatisticsPro.Blazor.GraphQl;
|
using WotBlitzStatisticsPro.Blazor.GraphQl;
|
||||||
|
using WotBlitzStatisticsPro.Blazor.Model;
|
||||||
|
|
||||||
namespace WotBlitzStatisticsPro.Blazor.Services.Mock
|
namespace WotBlitzStatisticsPro.Blazor.Services.Mock
|
||||||
{
|
{
|
||||||
@@ -388,5 +389,14 @@ namespace WotBlitzStatisticsPro.Blazor.Services.Mock
|
|||||||
return images[faker.Random.Number(images.Length - 1)];
|
return images[faker.Random.Number(images.Length - 1)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task<LoginInfo> ProlongToken(long accountId, string oldToken, RealmType realmType)
|
||||||
|
{
|
||||||
|
return Task.FromResult(new LoginInfo { AccountId = accountId, AccessToken = oldToken, Realm = realmType, ExpiresAt = 1631182800, NickName = "Fake nick"});
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<(IAccount accountInfo, IReadOnlyList<ISection> achievementsBySection)> CollectPlayerInfo(long accountId, RealmType realmType, string accessToken)
|
||||||
|
{
|
||||||
|
return GetPlayerInfo(accountId, realmType);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,23 +1,56 @@
|
|||||||
using System.Threading;
|
using System;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
using WotBlitzStatisticsPro.Blazor.Messages;
|
using WotBlitzStatisticsPro.Blazor.Messages;
|
||||||
|
using WotBlitzStatisticsPro.Blazor.Model;
|
||||||
|
|
||||||
namespace WotBlitzStatisticsPro.Blazor.Services
|
namespace WotBlitzStatisticsPro.Blazor.Services
|
||||||
{
|
{
|
||||||
public class NavigationMessagesInterceptor: INotificationHandler<OpenPlayerInfoMessage>
|
public class NavigationMessagesInterceptor: INotificationHandler<OpenPlayerInfoMessage>
|
||||||
{
|
{
|
||||||
private readonly NavigationManager _navigationManager;
|
private readonly NavigationManager _navigationManager;
|
||||||
|
private readonly IMediator _mediator;
|
||||||
|
private readonly ILocalStorageService _localStorageService;
|
||||||
|
|
||||||
public NavigationMessagesInterceptor(NavigationManager navigationManager)
|
public NavigationMessagesInterceptor(
|
||||||
|
NavigationManager navigationManager,
|
||||||
|
ILocalStorageService localStorageService,
|
||||||
|
IMediator mediator)
|
||||||
{
|
{
|
||||||
_navigationManager = navigationManager;
|
_navigationManager = navigationManager;
|
||||||
|
_localStorageService = localStorageService;
|
||||||
|
_mediator = mediator;
|
||||||
}
|
}
|
||||||
public Task Handle(OpenPlayerInfoMessage notification, CancellationToken cancellationToken)
|
public async Task Handle(OpenPlayerInfoMessage notification, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
if(notification.IsLoggedIn)
|
||||||
|
{
|
||||||
|
await CheckExpirationDateAndProlongIfNeeded();
|
||||||
|
}
|
||||||
_navigationManager.NavigateTo($"/player/{notification.AccountId}");
|
_navigationManager.NavigateTo($"/player/{notification.AccountId}");
|
||||||
return Task.CompletedTask;
|
}
|
||||||
|
|
||||||
|
private async Task CheckExpirationDateAndProlongIfNeeded()
|
||||||
|
{
|
||||||
|
int daysBeforeProlong = 5;
|
||||||
|
var loggedInData = await _localStorageService.GetItemAsync<LoginInfo>(Constants.LoginInfoLocalStorageKey);
|
||||||
|
|
||||||
|
if(loggedInData != null)
|
||||||
|
{
|
||||||
|
var tokenExpiration = (new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc))
|
||||||
|
.AddSeconds(loggedInData.ExpiresAt).ToLocalTime();
|
||||||
|
var expirationDaysDifference = (tokenExpiration - DateTime.Now).TotalDays;
|
||||||
|
if(expirationDaysDifference <= 0)
|
||||||
|
{
|
||||||
|
await _mediator.Publish(new RedirectToWgLoginMessage(loggedInData.Realm));
|
||||||
|
}
|
||||||
|
if(expirationDaysDifference > 0 && expirationDaysDifference <= daysBeforeProlong)
|
||||||
|
{
|
||||||
|
await _mediator.Publish(new ProlongWargamingAccessToken());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -13,7 +13,8 @@ namespace WotBlitzStatisticsPro.Blazor.Services
|
|||||||
public class WargamingLoginService:
|
public class WargamingLoginService:
|
||||||
INotificationHandler<LoginToWgMessage>,
|
INotificationHandler<LoginToWgMessage>,
|
||||||
INotificationHandler<RedirectToWgLoginMessage>,
|
INotificationHandler<RedirectToWgLoginMessage>,
|
||||||
INotificationHandler<RedirectFromWgLoginMessage>
|
INotificationHandler<RedirectFromWgLoginMessage>,
|
||||||
|
INotificationHandler<ProlongWargamingAccessToken>
|
||||||
{
|
{
|
||||||
private readonly IGraphQlBackendService _graphQlBackendService;
|
private readonly IGraphQlBackendService _graphQlBackendService;
|
||||||
private readonly DialogService _dialogService;
|
private readonly DialogService _dialogService;
|
||||||
@@ -68,5 +69,14 @@ namespace WotBlitzStatisticsPro.Blazor.Services
|
|||||||
// Send OpenPlayerInfoMessage with isLoggedIn = true
|
// Send OpenPlayerInfoMessage with isLoggedIn = true
|
||||||
await _mediator.Publish(new OpenPlayerInfoMessage(notification.AccountId, true));
|
await _mediator.Publish(new OpenPlayerInfoMessage(notification.AccountId, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task Handle(ProlongWargamingAccessToken notification, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var loggedInData = await _localStorage.GetItemAsync<LoginInfo>(Constants.LoginInfoLocalStorageKey);
|
||||||
|
var newLoginInfo = await _graphQlBackendService.ProlongToken(loggedInData.AccountId, loggedInData.AccessToken, loggedInData.Realm);
|
||||||
|
loggedInData.AccessToken = newLoginInfo.AccessToken;
|
||||||
|
loggedInData.ExpiresAt = newLoginInfo.ExpiresAt;
|
||||||
|
await _localStorage.SetItemAsync(Constants.LoginInfoLocalStorageKey, loggedInData);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user