Implement UploadFile() for Yandex, move models into their own files
This commit is contained in:
Родитель
35ad02c1cb
Коммит
847a21f256
|
@ -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; }
|
||||
}
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче