From a3e752459f568822677484f61445ed910fc20da8 Mon Sep 17 00:00:00 2001 From: artyom Date: Sat, 22 Dec 2018 22:57:41 +0300 Subject: [PATCH] Tests and usage for token storage --- .../AkavacheTokenStorageTests.cs | 50 +++++++++++++++++++ .../ProviderStorageTests.cs | 1 + Camelotia.Presentation.Uwp/App.xaml.cs | 1 + .../Views/MainPage.xaml | 2 +- .../Views/MainPage.xaml.cs | 7 +-- Camelotia.Services/Camelotia.Services.csproj | 2 +- .../Interfaces/ITokenStorage.cs | 9 +--- .../Providers/VkontakteFileSystemProvider.cs | 28 +++++++---- .../Providers/YandexFileSystemProvider.cs | 27 +++++++--- .../Storages/AkavacheTokenStorage.cs | 12 +++-- .../Storages/ProviderStorage.cs | 2 +- 11 files changed, 105 insertions(+), 36 deletions(-) create mode 100644 Camelotia.Presentation.Tests/AkavacheTokenStorageTests.cs diff --git a/Camelotia.Presentation.Tests/AkavacheTokenStorageTests.cs b/Camelotia.Presentation.Tests/AkavacheTokenStorageTests.cs new file mode 100644 index 0000000..158d707 --- /dev/null +++ b/Camelotia.Presentation.Tests/AkavacheTokenStorageTests.cs @@ -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("42"); + var result = await storage.ReadToken(); + result.Should().NotBeNull(); + result.Should().Be("42"); + } + + [Fact] + public async Task ShouldReadAndWriteNullsWithNoExceptions() + { + var storage = new AkavacheTokenStorage(); + await storage.WriteToken(null); + var result = await storage.ReadToken(); + result.Should().BeNull(); + } + + [Fact] + public async Task ShouldReadAndWriteMultipleTokens() + { + var storage = new AkavacheTokenStorage(); + await storage.WriteToken("vk"); + await storage.WriteToken("ya"); + var vk = await storage.ReadToken(); + var ya = await storage.ReadToken(); + vk.Should().Be("vk"); + ya.Should().Be("ya"); + } + + [Fact] + public async Task ShouldReturnNullIfAttemptingToReadTokenThatDoesNotExistYet() + { + var storage = new AkavacheTokenStorage(); + var response = await storage.ReadToken(); + response.Should().BeNull(); + } + } +} \ No newline at end of file diff --git a/Camelotia.Presentation.Tests/ProviderStorageTests.cs b/Camelotia.Presentation.Tests/ProviderStorageTests.cs index 5b820cf..246e81a 100644 --- a/Camelotia.Presentation.Tests/ProviderStorageTests.cs +++ b/Camelotia.Presentation.Tests/ProviderStorageTests.cs @@ -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; diff --git a/Camelotia.Presentation.Uwp/App.xaml.cs b/Camelotia.Presentation.Uwp/App.xaml.cs index 6b37b68..ac6d10a 100644 --- a/Camelotia.Presentation.Uwp/App.xaml.cs +++ b/Camelotia.Presentation.Uwp/App.xaml.cs @@ -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 { diff --git a/Camelotia.Presentation.Uwp/Views/MainPage.xaml b/Camelotia.Presentation.Uwp/Views/MainPage.xaml index 3d1fa53..0dfe331 100644 --- a/Camelotia.Presentation.Uwp/Views/MainPage.xaml +++ b/Camelotia.Presentation.Uwp/Views/MainPage.xaml @@ -1,5 +1,5 @@  { diff --git a/Camelotia.Services/Camelotia.Services.csproj b/Camelotia.Services/Camelotia.Services.csproj index aba6f64..424b5ce 100644 --- a/Camelotia.Services/Camelotia.Services.csproj +++ b/Camelotia.Services/Camelotia.Services.csproj @@ -7,7 +7,7 @@ - + diff --git a/Camelotia.Services/Interfaces/ITokenStorage.cs b/Camelotia.Services/Interfaces/ITokenStorage.cs index 0616dbd..660f5cb 100644 --- a/Camelotia.Services/Interfaces/ITokenStorage.cs +++ b/Camelotia.Services/Interfaces/ITokenStorage.cs @@ -4,13 +4,8 @@ namespace Camelotia.Services.Interfaces { public interface ITokenStorage { - Task WriteToken(AuthenticationTokenOwner owner, string token); + Task WriteToken(string token); - Task ReadToken(AuthenticationTokenOwner owner); - } - - public enum AuthenticationTokenOwner - { - Vkontakte, Yandex + Task ReadToken(); } } diff --git a/Camelotia.Services/Providers/VkontakteFileSystemProvider.cs b/Camelotia.Services/Providers/VkontakteFileSystemProvider.cs index 3e5a521..2eb017c 100644 --- a/Camelotia.Services/Providers/VkontakteFileSystemProvider.cs +++ b/Camelotia.Services/Providers/VkontakteFileSystemProvider.cs @@ -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 _isAuthorized; - private readonly ITokenStorage _tokenCache; - private VkApi _api; + private readonly ReplaySubject _isAuthorized = new ReplaySubject(); + 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(); + _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(_api.Token); _isAuthorized.OnNext(_api.IsAuthorized); } - public Task Logout() + public async Task Logout() { _api = new VkApi(); + await _tokenStorage.WriteToken(null); _isAuthorized.OnNext(_api.IsAuthorized); - return Task.CompletedTask; } public async Task> Get(string path) @@ -112,6 +114,14 @@ namespace Camelotia.Services.Providers } } + private async void EnsureLoggedInIfTokenSaved() + { + var token = await _tokenStorage.ReadToken(); + if (string.IsNullOrWhiteSpace(token) || _api.IsAuthorized) return; + await _api.AuthorizeAsync(new ApiAuthParams {AccessToken = token}); + _isAuthorized.OnNext(true); + } + private static async Task StreamToArray(Stream stream) { using (var memory = new MemoryStream()) diff --git a/Camelotia.Services/Providers/YandexFileSystemProvider.cs b/Camelotia.Services/Providers/YandexFileSystemProvider.cs index d57bfdf..c2d19e7 100644 --- a/Camelotia.Services/Providers/YandexFileSystemProvider.cs +++ b/Camelotia.Services/Providers/YandexFileSystemProvider.cs @@ -27,13 +27,14 @@ namespace Camelotia.Services.Providers private readonly ReplaySubject _isAuthorized = new ReplaySubject(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(null); _http.DefaultRequestHeaders.Clear(); _isAuthorized.OnNext(false); - return Task.CompletedTask; } public async Task OAuth() { var token = await GetAuthenticationToken(); + await _tokenStorage.WriteToken(token); + ApplyTokenToHeaders(token); + _isAuthorized.OnNext(true); + } + + private async void EnsureLoggedInIfTokenSaved() + { + var token = await _tokenStorage.ReadToken(); + 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 GetAuthenticationToken() diff --git a/Camelotia.Services/Storages/AkavacheTokenStorage.cs b/Camelotia.Services/Storages/AkavacheTokenStorage.cs index bb89cef..a0801dd 100644 --- a/Camelotia.Services/Storages/AkavacheTokenStorage.cs +++ b/Camelotia.Services/Storages/AkavacheTokenStorage.cs @@ -7,16 +7,18 @@ namespace Camelotia.Services.Storages { public sealed class AkavacheTokenStorage : ITokenStorage { - public async Task ReadToken(AuthenticationTokenOwner owner) + public AkavacheTokenStorage() => BlobCache.ApplicationName = "Camelotia"; + + public async Task ReadToken() { - var key = owner.ToString(); - var token = await BlobCache.Secure.GetObject(key); + var key = typeof(TOwner).Name; + var token = await BlobCache.Secure.GetOrCreateObject(key, () => null); return token; } - public async Task WriteToken(AuthenticationTokenOwner owner, string token) + public async Task WriteToken(string token) { - var key = owner.ToString(); + var key = typeof(TOwner).Name; await BlobCache.Secure.InsertObject(key, token); } } diff --git a/Camelotia.Services/Storages/ProviderStorage.cs b/Camelotia.Services/Storages/ProviderStorage.cs index d6caff6..4e34c65 100644 --- a/Camelotia.Services/Storages/ProviderStorage.cs +++ b/Camelotia.Services/Storages/ProviderStorage.cs @@ -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 {