Implement UploadFile() for Yandex, move models into their own files

This commit is contained in:
artyom 2018-12-20 11:58:00 +03:00
Родитель 35ad02c1cb
Коммит 847a21f256
14 изменённых файлов: 118 добавлений и 90 удалений

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

@ -53,10 +53,6 @@ namespace Camelotia.Presentation.Tests
}
[Fact]
public void ShouldImplementNonNullInitialPath()
{
_provider.InitialPath.Should().NotBeNull();
}
public void ShouldImplementNonNullInitialPath() => _provider.InitialPath.Should().NotBeNull();
}
}

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

@ -122,6 +122,22 @@ namespace Camelotia.Presentation.Tests
}
});
[Fact]
public void ShouldRefreshContentOfCurrentPathWhenFileIsUploaded() => new TestScheduler().With(scheduler =>
{
_provider.InitialPath.Returns(Separator);
_fileManager.OpenRead().Returns(("example", Stream.Null));
_authViewModel.IsAuthenticated.Returns(true);
var model = BuildProviderViewModel(scheduler);
model.CurrentPath.Should().Be(Separator);
model.UploadToCurrentPath.CanExecute(null).Should().BeTrue();
model.UploadToCurrentPath.Execute(null);
scheduler.AdvanceBy(2);
_provider.Received(1).Get(Separator);
});
private ProviderViewModel BuildProviderViewModel(IScheduler scheduler)
{
return new ProviderViewModel(_authViewModel, _fileManager, scheduler, scheduler, _provider);

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

@ -10,9 +10,6 @@ namespace Camelotia.Presentation.Tests
private readonly IProvider _provider = new VkontakteFileSystemProvider();
[Fact]
public void ShouldImplementNonNullInitialPath()
{
_provider.InitialPath.Should().NotBeNull();
}
public void ShouldImplementNonNullInitialPath() => _provider.InitialPath.Should().NotBeNull();
}
}

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

@ -10,9 +10,6 @@ namespace Camelotia.Presentation.Tests
private readonly IProvider _provider = new YandexFileSystemProvider();
[Fact]
public void ShouldImplementNonNullInitialPath()
{
_provider.InitialPath.Should().NotBeNull();
}
public void ShouldImplementNonNullInitialPath() => _provider.InitialPath.Should().NotBeNull();
}
}

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

@ -113,11 +113,14 @@ namespace Camelotia.Presentation.ViewModels
_uploadToCurrentPath = ReactiveCommand.CreateFromObservable(
() => Observable
.FromAsync(fileManager.OpenRead)
.Where(response => response.Name != null && response.Stream != null)
.Select(x => _provider.UploadFile(CurrentPath, x.Stream, x.Name))
.SelectMany(task => task.ToObservable()),
canUploadToCurrentPath,
mainThread);
_uploadToCurrentPath.InvokeCommand(_refresh);
var canDownloadSelectedFile = this
.WhenAnyValue(x => x.SelectedFile)
.Select(file => file != null && !file.IsFolder && !file.IsDrive)
@ -126,6 +129,7 @@ namespace Camelotia.Presentation.ViewModels
_downloadSelectedFile = ReactiveCommand.CreateFromObservable(
() => Observable
.FromAsync(() => fileManager.OpenWrite(SelectedFile.Name))
.Where(stream => stream != null)
.Select(stream => _provider.DownloadFile(SelectedFile.Path, stream))
.SelectMany(task => task.ToObservable()),
canDownloadSelectedFile,
@ -174,6 +178,8 @@ namespace Camelotia.Presentation.ViewModels
[Reactive] public FileModel SelectedFile { get; set; }
public string CurrentPath => _currentPath?.Value ?? _provider.InitialPath;
public ICommand DownloadSelectedFile => _downloadSelectedFile;
public ICommand UploadToCurrentPath => _uploadToCurrentPath;
@ -184,8 +190,6 @@ namespace Camelotia.Presentation.ViewModels
public string Description => _provider.Description;
public string CurrentPath => _currentPath?.Value ?? _provider.InitialPath;
public bool CanLogout => _canLogout.Value;
public bool IsLoading => _isLoading.Value;

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

@ -10,6 +10,7 @@ namespace Camelotia.Services.Models
IsDrive = isDrive;
Size = size;
}
public string Name { get; }
public string Path { get; }

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

@ -0,0 +1,23 @@
using System;
using Newtonsoft.Json;
namespace Camelotia.Services.Models.Yandex
{
internal class YandexContentItemResponse
{
[JsonProperty("path")]
public string Path { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("size")]
public long Size { get; set; }
[JsonProperty("created")]
public DateTime Created { get; set; }
}
}

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

@ -0,0 +1,11 @@
using System.Collections.Generic;
using Newtonsoft.Json;
namespace Camelotia.Services.Models.Yandex
{
internal class YandexContentItemsResponse
{
[JsonProperty("items")]
public IList<YandexContentItemResponse> Items { get; set; }
}
}

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

@ -0,0 +1,10 @@
using Newtonsoft.Json;
namespace Camelotia.Services.Models.Yandex
{
internal class YandexContentResponse
{
[JsonProperty("_embedded")]
public YandexContentItemsResponse Embedded { get; set; }
}
}

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

@ -0,0 +1,10 @@
using Newtonsoft.Json;
namespace Camelotia.Services.Models.Yandex
{
internal class YandexFileLoadResponse
{
[JsonProperty("href")]
public string Href { get; set; }
}
}

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

@ -0,0 +1,10 @@
using Newtonsoft.Json;
namespace Camelotia.Services.Models.Yandex
{
internal class YandexTokenAuthResponse
{
[JsonProperty("access_token")]
public string AccessToken { get; set; }
}
}

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

@ -57,35 +57,27 @@ namespace Camelotia.Services.Providers
.AsEnumerable();
});
public Task DownloadFile(string from, Stream to)
public async Task DownloadFile(string from, Stream to)
{
if (from == null) throw new ArgumentNullException(nameof(from));
if (to == null) throw new ArgumentNullException(nameof(to));
if (IsDirectory(from)) throw new InvalidOperationException("Can't download directory.");
using (var fileStream = File.OpenRead(from))
{
fileStream.Seek(0, SeekOrigin.Begin);
fileStream.CopyTo(to);
await fileStream.CopyToAsync(to);
}
}
return Task.CompletedTask;
}
public Task UploadFile(string to, Stream from, string name)
public async Task UploadFile(string to, Stream from, string name)
{
if (to == null) throw new ArgumentNullException(nameof(to));
if (from == null) throw new ArgumentNullException(nameof(from));
if (!IsDirectory(to)) throw new InvalidOperationException("Can't upload to a non-directory.");
var path = Path.Combine(to, name);
using (var fileStream = File.Create(path))
{
from.Seek(0, SeekOrigin.Begin);
from.CopyTo(fileStream);
await from.CopyToAsync(fileStream);
}
return Task.CompletedTask;
}
private static string GetSizeOnAllDisks()
@ -109,7 +101,6 @@ namespace Camelotia.Services.Providers
private static bool IsDirectory(string path)
{
var attributes = File.GetAttributes(path);
return attributes.HasFlag(FileAttributes.Directory);
}
}

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

@ -68,8 +68,6 @@ namespace Camelotia.Services.Providers
public async Task<IEnumerable<FileModel>> Get(string path)
{
if (path == null) throw new ArgumentNullException(nameof(path));
var documents = await _api.Docs.GetAsync();
return documents.Select(document =>
{
@ -82,9 +80,6 @@ namespace Camelotia.Services.Providers
public async Task DownloadFile(string from, Stream to)
{
if (from == null) throw new ArgumentNullException(nameof(from));
if (to == null) throw new ArgumentNullException(nameof(to));
var isValidUriString = Uri.IsWellFormedUriString(from, UriKind.Absolute);
if (!isValidUriString) throw new InvalidOperationException("Uri is invalid.");
@ -96,10 +91,6 @@ namespace Camelotia.Services.Providers
public async Task UploadFile(string to, Stream from, string name)
{
if (to == null) throw new ArgumentNullException(nameof(to));
if (from == null) throw new ArgumentNullException(nameof(from));
if (name == null) throw new ArgumentNullException(nameof(name));
var server = await _api.Docs.GetUploadServerAsync().ConfigureAwait(false);
var uri = new Uri(server.UploadUrl);

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

@ -11,6 +11,7 @@ using System.Text;
using System.Threading.Tasks;
using Camelotia.Services.Interfaces;
using Camelotia.Services.Models;
using Camelotia.Services.Models.Yandex;
using Newtonsoft.Json;
namespace Camelotia.Services.Providers
@ -19,6 +20,7 @@ namespace Camelotia.Services.Providers
{
private const string YandexAuthTokenUrl = "https://oauth.yandex.ru/token";
private const string CloudApiDownloadFileUrl = "https://cloud-api.yandex.net/v1/disk/resources/download?path=";
private const string CloudApiUploadFileUrl = "https://cloud-api.yandex.net/v1/disk/resources/upload?path=";
private const string CloudApiGetPathBase = "https://cloud-api.yandex.net:443/v1/disk/resources?path=";
private const string SuccessContent = "<html><body>Please return to the app.</body></html>";
private const string ClientSecret = "f14bfc0275a34ceea83d7de7f4b50898";
@ -45,8 +47,6 @@ namespace Camelotia.Services.Providers
public async Task<IEnumerable<FileModel>> Get(string path)
{
if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException(nameof(path));
var yaPath = path.Replace("\\", "/");
var encodedPath = WebUtility.UrlEncode(yaPath);
var pathUrl = CloudApiGetPathBase + encodedPath;
@ -70,24 +70,37 @@ namespace Camelotia.Services.Providers
public async Task DownloadFile(string from, Stream to)
{
if (from == null) throw new ArgumentNullException(nameof(from));
if (to == null) throw new ArgumentNullException(nameof(to));
var encodedPath = WebUtility.UrlEncode(from);
var yaPath = from.Replace("\\", "/");
var encodedPath = WebUtility.UrlEncode(yaPath);
var pathUrl = CloudApiDownloadFileUrl + encodedPath;
using (var response = await _http.GetAsync(pathUrl).ConfigureAwait(false))
{
var json = await response.Content.ReadAsStringAsync();
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
var content = JsonConvert.DeserializeObject<YandexFileLoadResponse>(json);
var content = JsonConvert.DeserializeObject<YandexFileDownloadResponse>(json);
using (var file = await _http.GetAsync(content.Href).ConfigureAwait(false))
using (var stream = await file.Content.ReadAsStreamAsync().ConfigureAwait(false))
await stream.CopyToAsync(to).ConfigureAwait(false);
}
}
public Task UploadFile(string to, Stream from, string name) => Task.CompletedTask;
public async Task UploadFile(string to, Stream from, string name)
{
var yaPath = Path.Combine(to, name).Replace("\\", "/");
var encodedPath = WebUtility.UrlEncode(yaPath);
var pathUrl = CloudApiUploadFileUrl + encodedPath;
using (var response = await _http.GetAsync(pathUrl).ConfigureAwait(false))
{
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
var content = JsonConvert.DeserializeObject<YandexFileLoadResponse>(json);
var httpContent = new StreamContent(from);
using (var file = await _http.PutAsync(content.Href, httpContent).ConfigureAwait(false))
file.EnsureSuccessStatusCode();
}
}
public Task Logout()
{
@ -159,47 +172,5 @@ namespace Camelotia.Services.Providers
return "https://oauth.yandex.ru/authorize?response_type=code" +
$"&client_id={ClientId}&redirect_url={redirect}";
}
private class YandexTokenAuthResponse
{
[JsonProperty("access_token")]
public string AccessToken { get; set; }
}
private class YandexContentResponse
{
[JsonProperty("_embedded")]
public YandexContentItemsResponse Embedded { get; set; }
}
private class YandexContentItemsResponse
{
[JsonProperty("items")]
public IList<YandexContentItemResponse> Items { get; set; }
}
private class YandexContentItemResponse
{
[JsonProperty("path")]
public string Path { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("size")]
public long Size { get; set; }
[JsonProperty("created")]
public DateTime Created { get; set; }
}
private class YandexFileDownloadResponse
{
[JsonProperty("href")]
public string Href { get; set; }
}
}
}