Tests and usage for token storage

This commit is contained in:
artyom 2018-12-22 22:57:41 +03:00
Родитель 509fafa510
Коммит a3e752459f
11 изменённых файлов: 105 добавлений и 36 удалений

Просмотреть файл

@ -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
{