Tests and usage for token storage
This commit is contained in:
Родитель
509fafa510
Коммит
a3e752459f
|
@ -0,0 +1,50 @@
|
|||
using System.Threading.Tasks;
|
||||
using Camelotia.Services.Providers;
|
||||
using Camelotia.Services.Storages;
|
||||
using FluentAssertions;
|
||||
using Xunit;
|
||||
|
||||
namespace Camelotia.Presentation.Tests
|
||||
{
|
||||
public sealed class AkavacheTokenStorageTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task ShouldReadAndWriteTokens()
|
||||
{
|
||||
var storage = new AkavacheTokenStorage();
|
||||
await storage.WriteToken<VkontakteFileSystemProvider>("42");
|
||||
var result = await storage.ReadToken<VkontakteFileSystemProvider>();
|
||||
result.Should().NotBeNull();
|
||||
result.Should().Be("42");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ShouldReadAndWriteNullsWithNoExceptions()
|
||||
{
|
||||
var storage = new AkavacheTokenStorage();
|
||||
await storage.WriteToken<VkontakteFileSystemProvider>(null);
|
||||
var result = await storage.ReadToken<VkontakteFileSystemProvider>();
|
||||
result.Should().BeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ShouldReadAndWriteMultipleTokens()
|
||||
{
|
||||
var storage = new AkavacheTokenStorage();
|
||||
await storage.WriteToken<VkontakteFileSystemProvider>("vk");
|
||||
await storage.WriteToken<YandexFileSystemProvider>("ya");
|
||||
var vk = await storage.ReadToken<VkontakteFileSystemProvider>();
|
||||
var ya = await storage.ReadToken<YandexFileSystemProvider>();
|
||||
vk.Should().Be("vk");
|
||||
ya.Should().Be("ya");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ShouldReturnNullIfAttemptingToReadTokenThatDoesNotExistYet()
|
||||
{
|
||||
var storage = new AkavacheTokenStorage();
|
||||
var response = await storage.ReadToken<AkavacheTokenStorageTests>();
|
||||
response.Should().BeNull();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ using System.Linq;
|
|||
using System.Threading.Tasks;
|
||||
using Camelotia.Services.Interfaces;
|
||||
using Camelotia.Services.Providers;
|
||||
using Camelotia.Services.Storages;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ using Windows.ApplicationModel.Activation;
|
|||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
using Camelotia.Presentation.Uwp.Views;
|
||||
|
||||
namespace Camelotia.Presentation.Uwp
|
||||
{
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<Page
|
||||
x:Class="Camelotia.Presentation.Uwp.MainView"
|
||||
x:Class="Camelotia.Presentation.Uwp.Views.MainView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:views="using:Camelotia.Presentation.Uwp.Views"
|
||||
|
|
|
@ -1,9 +1,4 @@
|
|||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml;
|
||||
using ReactiveUI;
|
||||
using Camelotia.Presentation.Interfaces;
|
||||
|
||||
namespace Camelotia.Presentation.Uwp
|
||||
namespace Camelotia.Presentation.Uwp.Views
|
||||
{
|
||||
public sealed partial class MainView : Page, IViewFor<IMainViewModel>
|
||||
{
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<ItemGroup>
|
||||
<PackageReference Include="akavache" Version="6.0.31" />
|
||||
<PackageReference Include="System.Reactive" Version="4.0.0" />
|
||||
<PackageReference Include="VkNet" Version="1.39.0" />
|
||||
<PackageReference Include="VkNet" Version="1.40.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -4,13 +4,8 @@ namespace Camelotia.Services.Interfaces
|
|||
{
|
||||
public interface ITokenStorage
|
||||
{
|
||||
Task WriteToken(AuthenticationTokenOwner owner, string token);
|
||||
Task WriteToken<TOwner>(string token);
|
||||
|
||||
Task<string> ReadToken(AuthenticationTokenOwner owner);
|
||||
}
|
||||
|
||||
public enum AuthenticationTokenOwner
|
||||
{
|
||||
Vkontakte, Yandex
|
||||
Task<string> ReadToken<TOwner>();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,21 +13,21 @@ using Newtonsoft.Json;
|
|||
using VkNet.Enums.Filters;
|
||||
using VkNet.Model;
|
||||
using VkNet;
|
||||
using VkNet.Abstractions;
|
||||
|
||||
namespace Camelotia.Services.Providers
|
||||
{
|
||||
public sealed class VkontakteFileSystemProvider : IProvider
|
||||
{
|
||||
private readonly ReplaySubject<bool> _isAuthorized;
|
||||
private readonly ITokenStorage _tokenCache;
|
||||
private VkApi _api;
|
||||
private readonly ReplaySubject<bool> _isAuthorized = new ReplaySubject<bool>();
|
||||
private readonly ITokenStorage _tokenStorage;
|
||||
private IVkApi _api = new VkApi();
|
||||
|
||||
public VkontakteFileSystemProvider(ITokenStorage tokenCache)
|
||||
public VkontakteFileSystemProvider(ITokenStorage tokenStorage)
|
||||
{
|
||||
_api = new VkApi();
|
||||
_tokenCache = tokenCache;
|
||||
_isAuthorized = new ReplaySubject<bool>();
|
||||
_tokenStorage = tokenStorage;
|
||||
_isAuthorized.OnNext(false);
|
||||
EnsureLoggedInIfTokenSaved();
|
||||
}
|
||||
|
||||
public string Size => "Unknown";
|
||||
|
@ -58,14 +58,16 @@ namespace Camelotia.Services.Providers
|
|||
Password = password,
|
||||
Settings = Settings.Documents
|
||||
});
|
||||
|
||||
await _tokenStorage.WriteToken<VkontakteFileSystemProvider>(_api.Token);
|
||||
_isAuthorized.OnNext(_api.IsAuthorized);
|
||||
}
|
||||
|
||||
public Task Logout()
|
||||
public async Task Logout()
|
||||
{
|
||||
_api = new VkApi();
|
||||
await _tokenStorage.WriteToken<VkontakteFileSystemProvider>(null);
|
||||
_isAuthorized.OnNext(_api.IsAuthorized);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<FileModel>> Get(string path)
|
||||
|
@ -112,6 +114,14 @@ namespace Camelotia.Services.Providers
|
|||
}
|
||||
}
|
||||
|
||||
private async void EnsureLoggedInIfTokenSaved()
|
||||
{
|
||||
var token = await _tokenStorage.ReadToken<VkontakteFileSystemProvider>();
|
||||
if (string.IsNullOrWhiteSpace(token) || _api.IsAuthorized) return;
|
||||
await _api.AuthorizeAsync(new ApiAuthParams {AccessToken = token});
|
||||
_isAuthorized.OnNext(true);
|
||||
}
|
||||
|
||||
private static async Task<byte[]> StreamToArray(Stream stream)
|
||||
{
|
||||
using (var memory = new MemoryStream())
|
||||
|
|
|
@ -27,13 +27,14 @@ namespace Camelotia.Services.Providers
|
|||
private readonly ReplaySubject<bool> _isAuthorized = new ReplaySubject<bool>(1);
|
||||
private readonly HttpClient _http = new HttpClient();
|
||||
private readonly IAuthenticator _authenticator;
|
||||
private readonly ITokenStorage _tokenCache;
|
||||
private readonly ITokenStorage _tokenStorage;
|
||||
|
||||
public YandexFileSystemProvider(IAuthenticator authenticator, ITokenStorage tokenCache)
|
||||
public YandexFileSystemProvider(IAuthenticator authenticator, ITokenStorage tokenStorage)
|
||||
{
|
||||
_authenticator = authenticator;
|
||||
_tokenStorage = tokenStorage;
|
||||
_isAuthorized.OnNext(false);
|
||||
_tokenCache = tokenCache;
|
||||
EnsureLoggedInIfTokenSaved();
|
||||
}
|
||||
|
||||
public string Size => "Unknown";
|
||||
|
@ -112,20 +113,34 @@ namespace Camelotia.Services.Providers
|
|||
}
|
||||
}
|
||||
|
||||
public Task Logout()
|
||||
public async Task Logout()
|
||||
{
|
||||
await _tokenStorage.WriteToken<YandexFileSystemProvider>(null);
|
||||
_http.DefaultRequestHeaders.Clear();
|
||||
_isAuthorized.OnNext(false);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task OAuth()
|
||||
{
|
||||
var token = await GetAuthenticationToken();
|
||||
await _tokenStorage.WriteToken<YandexFileSystemProvider>(token);
|
||||
ApplyTokenToHeaders(token);
|
||||
_isAuthorized.OnNext(true);
|
||||
}
|
||||
|
||||
private async void EnsureLoggedInIfTokenSaved()
|
||||
{
|
||||
var token = await _tokenStorage.ReadToken<YandexFileSystemProvider>();
|
||||
if (string.IsNullOrWhiteSpace(token)) return;
|
||||
ApplyTokenToHeaders(token);
|
||||
_isAuthorized.OnNext(true);
|
||||
}
|
||||
|
||||
private void ApplyTokenToHeaders(string token)
|
||||
{
|
||||
_http.DefaultRequestHeaders.Clear();
|
||||
_http.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||
_http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("OAuth", token);
|
||||
_isAuthorized.OnNext(true);
|
||||
}
|
||||
|
||||
private async Task<string> GetAuthenticationToken()
|
||||
|
|
|
@ -7,16 +7,18 @@ namespace Camelotia.Services.Storages
|
|||
{
|
||||
public sealed class AkavacheTokenStorage : ITokenStorage
|
||||
{
|
||||
public async Task<string> ReadToken(AuthenticationTokenOwner owner)
|
||||
public AkavacheTokenStorage() => BlobCache.ApplicationName = "Camelotia";
|
||||
|
||||
public async Task<string> ReadToken<TOwner>()
|
||||
{
|
||||
var key = owner.ToString();
|
||||
var token = await BlobCache.Secure.GetObject<string>(key);
|
||||
var key = typeof(TOwner).Name;
|
||||
var token = await BlobCache.Secure.GetOrCreateObject<string>(key, () => null);
|
||||
return token;
|
||||
}
|
||||
|
||||
public async Task WriteToken(AuthenticationTokenOwner owner, string token)
|
||||
public async Task WriteToken<TOwner>(string token)
|
||||
{
|
||||
var key = owner.ToString();
|
||||
var key = typeof(TOwner).Name;
|
||||
await BlobCache.Secure.InsertObject(key, token);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ using System.Collections.Generic;
|
|||
using System.Threading.Tasks;
|
||||
using Camelotia.Services.Interfaces;
|
||||
|
||||
namespace Camelotia.Services.Providers
|
||||
namespace Camelotia.Services.Storages
|
||||
{
|
||||
public sealed class ProviderStorage : IProviderStorage
|
||||
{
|
||||
|
|
Загрузка…
Ссылка в новой задаче