refactoring thirdParty

This commit is contained in:
Anton Suhorukov 2023-02-16 18:16:41 +03:00
parent d0f5f29a3b
commit 858e0dc300
18 changed files with 773 additions and 1543 deletions

View File

@ -28,7 +28,7 @@ namespace ASC.Files.Core;
public interface IProviderInfo : IDisposable
{
int ID { get; set; }
int ProviderId { get; set; }
string ProviderKey { get; }
Guid Owner { get; }
FolderType RootFolderType { get; }

View File

@ -0,0 +1,47 @@
// (c) Copyright Ascensio System SIA 2010-2022
//
// This program is a free software product.
// You can redistribute it and/or modify it under the terms
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
// any third-party rights.
//
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
//
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
//
// The interactive user interfaces in modified source and object code versions of the Program must
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
//
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
// trademark law for use of our trademarks.
//
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
using File = System.IO.File;
namespace ASC.Files.Core.Core.Thirdparty;
internal class AbstractFileDao<TFile, TFolder, TItem> : IFileDao<string>
{
private readonly IDaoBase<TFile, TFolder, TItem> _dao;
private readonly IProviderInfo _providerInfo;
public async Task InvalidateCacheAsync(string fileId)
{
var boxFileId = _dao.MakeId(fileId);
await _providerInfo.CacheResetAsync(boxFileId, true);
var boxFile = await GetBoxFileAsync(fileId);
var parentPath = GetParentFolderId(boxFile);
if (parentPath != null)
{
await ProviderInfo.CacheResetAsync(parentPath);
}
}
}

View File

@ -0,0 +1,327 @@
// (c) Copyright Ascensio System SIA 2010-2022
//
// This program is a free software product.
// You can redistribute it and/or modify it under the terms
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
// any third-party rights.
//
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
//
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
//
// The interactive user interfaces in modified source and object code versions of the Program must
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
//
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
// trademark law for use of our trademarks.
//
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
namespace ASC.Files.Core.Core.Thirdparty;
public abstract class AbstractProviderInfo<TFile, TFolder, TItem, TProvider> : IProviderInfo
where TFile : class
where TFolder : class
where TItem : class
where TProvider : Consumer, IOAuthProvider, new()
{
internal abstract string Selector { get; }
public DisposableWrapper Wrapper { get; }
public AbstractProviderInfo(DisposableWrapper wrapper)
{
Wrapper = wrapper;
}
public DateTime CreateOn { get; set; }
public string CustomerTitle { get; set; }
public string FolderId { get; set; }
public FolderType FolderType { get; set; }
public bool HasLogo { get; set; }
public int ProviderId { get; set; }
public Guid Owner { get; set; }
public bool Private { get; set; }
public string ProviderKey { get; set; }
public string RootFolderId => $"{Selector}-" + ProviderId;
public FolderType RootFolderType { get; set; }
public OAuth20Token Token { get; set; }
internal bool StorageOpened => Wrapper.TryGetStorage(ProviderId, out var storage) && storage.IsOpened;
private readonly ProviderInfoHelper _providerInfoHelper;
internal Task<IThirdPartyStorage<TFile, TFolder, TItem>> StorageAsync
{
get
{
if (!Wrapper.TryGetStorage<IThirdPartyStorage<TFile, TFolder, TItem>>(ProviderId, out var storage) || !storage.IsOpened)
{
return Wrapper.CreateStorageAsync<IThirdPartyStorage<TFile, TFolder, TItem>, TProvider>(Token, ProviderId);
}
return Task.FromResult(storage);
}
}
public async Task<bool> CheckAccessAsync()
{
var storage = await StorageAsync;
return await storage.CheckAccessAsync();
}
public void Dispose()
{
if (StorageOpened)
{
StorageAsync.Result.Close();
}
}
public Task InvalidateStorageAsync()
{
if (Wrapper != null)
{
Wrapper.Dispose();
}
return CacheResetAsync();
}
public void UpdateTitle(string newtitle)
{
CustomerTitle = newtitle;
}
internal Task CacheResetAsync(string id = null, bool? isFile = null)
{
return _providerInfoHelper.CacheResetAsync(ProviderId, id, isFile);
}
internal async Task<TFile> GetFileAsync(string fileId)
{
var storage = await StorageAsync;
return await _providerInfoHelper.GetFileAsync(storage, ProviderId, fileId, Selector);
}
internal async Task<TFolder> GetFolderAsync(string folderId)
{
var storage = await StorageAsync;
return await _providerInfoHelper.GetFolderAsync(storage, ProviderId, folderId, Selector);
}
internal async Task<List<TItem>> GetItemsAsync(string folderId)
{
var storage = await StorageAsync;
return await _providerInfoHelper.GetItemsAsync(storage, ProviderId, folderId, Selector);
}
internal async Task<Stream> GetThumbnailAsync(string fileId, int width, int height)
{
var storage = await StorageAsync;
return await storage.GetThumbnailAsync(fileId, width, height);
}
}
[Singletone]
public class ProviderInfoHelper
{
private readonly ICache _cacheChildItems;
private readonly TimeSpan _cacheExpiration = TimeSpan.FromMinutes(1);
private readonly ICache _cacheFile;
private readonly ICache _cacheFolder;
private readonly ICacheNotify<BoxCacheItem> _cacheNotify;
private readonly IEnumerable<string> _selectors = Selectors.StoredCache.Select(s => s.Id);
public ProviderInfoHelper(ICacheNotify<BoxCacheItem> cacheNotify, ICache cache)
{
_cacheFile = cache;
_cacheFolder = cache;
_cacheChildItems = cache;
_cacheNotify = cacheNotify;
foreach (var selector in _selectors)
{
_cacheNotify.Subscribe((i) =>
{
if (i.ResetAll)
{
_cacheChildItems.Remove(new Regex($"^{selector}-" + i.Key + ".*"));
_cacheFile.Remove(new Regex($"^{selector}f-" + i.Key + ".*"));
_cacheFolder.Remove(new Regex($"^{selector}d-" + i.Key + ".*"));
}
if (!i.IsFileExists)
{
_cacheChildItems.Remove($"{selector}-" + i.Key);
_cacheFolder.Remove($"{selector}d-" + i.Key);
}
else
{
if (i.IsFile)
{
_cacheFile.Remove($"{selector}f-" + i.Key);
}
else
{
_cacheFolder.Remove($"{selector}d-" + i.Key);
}
}
}, CacheNotifyAction.Remove);
}
}
internal async Task CacheResetAsync(int ThitdId, string id = null, bool? isFile = null)
{
var key = ThitdId + "-";
if (id == null)
{
await _cacheNotify.PublishAsync(new BoxCacheItem { ResetAll = true, Key = key }, CacheNotifyAction.Remove);
}
else
{
key += id;
await _cacheNotify.PublishAsync(new BoxCacheItem { IsFile = isFile ?? false, IsFileExists = isFile.HasValue, Key = key }, CacheNotifyAction.Remove);
}
}
internal async ValueTask<TFile> GetFileAsync<TFile>(IThirdPartyFileStorage<TFile> storage, int id, string fileId, string selector) where TFile : class
{
var file = _cacheFile.Get<TFile>($"{selector}f-" + id + "-" + fileId);
if (file == null)
{
file = await storage.GetFileAsync(fileId);
if (file != null)
{
_cacheFile.Insert($"{selector}f-" + id + "-" + fileId, file, DateTime.UtcNow.Add(_cacheExpiration));
}
}
return file;
}
internal async Task<TFolder> GetFolderAsync<TFolder>(IThirdPartyFolderStorage<TFolder> storage, int id, string folderId, string selector) where TFolder : class
{
var folder = _cacheFolder.Get<TFolder>($"{selector}d-" + id + "-" + folderId);
if (folder == null)
{
folder = await storage.GetFolderAsync(folderId);
if (folder != null)
{
_cacheFolder.Insert($"{selector}d-" + id + "-" + folderId, folder, DateTime.UtcNow.Add(_cacheExpiration));
}
}
return folder;
}
internal async Task<List<TItem>> GetItemsAsync<TItem>(IThirdPartyItemStorage<TItem> storage, int id, string folderId, string selector) where TItem : class
{
var items = _cacheChildItems.Get<List<TItem>>($"{selector}-" + id + "-" + folderId);
if (items == null)
{
items = await storage.GetItemsAsync(folderId);
_cacheChildItems.Insert($"{selector}-" + id + "-" + folderId, items, DateTime.UtcNow.Add(_cacheExpiration));
}
return items;
}
}
[Transient]
public class DisposableWrapper : IDisposable
{
private readonly ConsumerFactory _consumerFactory;
private readonly OAuth20TokenHelper _oAuth20TokenHelper;
private readonly IServiceProvider _serviceProvider;
private readonly ConcurrentDictionary<int, IThirdPartyStorage> _storages =
new ConcurrentDictionary<int, IThirdPartyStorage>();
public DisposableWrapper(ConsumerFactory consumerFactory, IServiceProvider serviceProvider, OAuth20TokenHelper oAuth20TokenHelper)
{
_consumerFactory = consumerFactory;
_serviceProvider = serviceProvider;
_oAuth20TokenHelper = oAuth20TokenHelper;
}
public void Dispose()
{
foreach (var (key, storage) in _storages)
{
storage.Close();
_storages.Remove(key, out _);
}
}
internal Task<T> CreateStorageAsync<T, T1>(OAuth20Token token, int id)
where T : IThirdPartyStorage
where T1 : Consumer, IOAuthProvider, new()
{
if (TryGetStorage<T>(id, out var storage) && storage.IsOpened)
{
return Task.FromResult(storage);
}
return InternalCreateStorageAsync<T, T1>(token, id);
}
internal bool TryGetStorage<T>(int providerId, out T storage)
{
var result = _storages.TryGetValue(providerId, out var s);
storage = (T)s;
return result;
}
internal bool TryGetStorage(int providerId, out IThirdPartyStorage storage)
{
return _storages.TryGetValue(providerId, out storage);
}
private Task CheckTokenAsync<T>(OAuth20Token token, int id) where T : Consumer, IOAuthProvider, new()
{
if (token == null)
{
throw new UnauthorizedAccessException("Cannot create third party session with given token");
}
return InternalCheckTokenAsync<T>(token, id);
}
private async Task InternalCheckTokenAsync<T>(OAuth20Token token, int id) where T : Consumer, IOAuthProvider, new()
{
if (token.IsExpired)
{
token = _oAuth20TokenHelper.RefreshToken<T>(_consumerFactory, token);
var dbDao = _serviceProvider.GetService<ProviderAccountDao>();
await dbDao.UpdateProviderInfoAsync(id, new AuthData(token: token.ToJson()));
}
}
private async Task<T> InternalCreateStorageAsync<T, T1>(OAuth20Token token, int id)
where T : IThirdPartyStorage
where T1 : Consumer, IOAuthProvider, new()
{
var storage = _serviceProvider.GetService<T>();
await CheckTokenAsync<T1>(token, id);
storage.Open(token);
_storages.TryAdd(id, storage);
return storage;
}
}

View File

@ -26,7 +26,7 @@
namespace ASC.Files.Thirdparty.Box;
internal abstract class BoxDaoBase : ThirdPartyProviderDao<BoxProviderInfo>
internal abstract class BoxDaoBase : ThirdPartyProviderDao<BoxProviderInfo>, IDaoBase<BoxFile, BoxFolder, BoxItem>
{
protected override string Id => Selectors.Box.Id;
@ -44,7 +44,7 @@ internal abstract class BoxDaoBase : ThirdPartyProviderDao<BoxProviderInfo>
{
}
protected static string MakeBoxId(object entryId)
public string MakeId(object entryId)
{
var id = Convert.ToString(entryId, CultureInfo.InvariantCulture);
@ -53,14 +53,14 @@ internal abstract class BoxDaoBase : ThirdPartyProviderDao<BoxProviderInfo>
: id.TrimStart('/');
}
protected static string GetParentFolderId(BoxItem boxItem)
public string GetParentFolderId(BoxItem boxItem)
{
return boxItem == null || boxItem.Parent == null
? null
: boxItem.Parent.Id;
}
protected string MakeId(BoxItem boxItem)
public string MakeId(BoxItem boxItem)
{
var path = string.Empty;
if (boxItem != null)
@ -71,14 +71,14 @@ internal abstract class BoxDaoBase : ThirdPartyProviderDao<BoxProviderInfo>
return MakeId(path);
}
protected override string MakeId(string path = null)
public override string MakeId(string path = null)
{
var p = string.IsNullOrEmpty(path) || path == "0" ? "" : ("-|" + path.TrimStart('/'));
return $"{PathPrefix}{p}";
}
protected string MakeFolderTitle(BoxFolder boxFolder)
public string MakeFolderTitle(BoxFolder boxFolder)
{
if (boxFolder == null || IsRoot(boxFolder))
{
@ -88,7 +88,7 @@ internal abstract class BoxDaoBase : ThirdPartyProviderDao<BoxProviderInfo>
return Global.ReplaceInvalidCharsAndTruncate(boxFolder.Name);
}
protected string MakeFileTitle(BoxFile boxFile)
public string MakeFileTitle(BoxFile boxFile)
{
if (boxFile == null || string.IsNullOrEmpty(boxFile.Name))
{
@ -98,7 +98,7 @@ internal abstract class BoxDaoBase : ThirdPartyProviderDao<BoxProviderInfo>
return Global.ReplaceInvalidCharsAndTruncate(boxFile.Name);
}
protected Folder<string> ToFolder(BoxFolder boxFolder)
public Folder<string> ToFolder(BoxFolder boxFolder)
{
if (boxFolder == null)
{
@ -140,7 +140,7 @@ internal abstract class BoxDaoBase : ThirdPartyProviderDao<BoxProviderInfo>
return folder;
}
protected static bool IsRoot(BoxFolder boxFolder)
public static bool IsRoot(BoxFolder boxFolder)
{
return boxFolder.Id == "0";
}
@ -201,17 +201,17 @@ internal abstract class BoxDaoBase : ThirdPartyProviderDao<BoxProviderInfo>
return file;
}
public async Task<Folder<string>> GetRootFolderAsync(string folderId)
public async Task<Folder<string>> GetRootFolderAsync()
{
return ToFolder(await GetBoxFolderAsync("0"));
return ToFolder(await GetFolderAsync("0"));
}
protected async Task<BoxFolder> GetBoxFolderAsync(string folderId)
public async Task<BoxFolder> GetFolderAsync(string folderId)
{
var boxFolderId = MakeBoxId(folderId);
var boxFolderId = MakeId(folderId);
try
{
var folder = await ProviderInfo.GetBoxFolderAsync(boxFolderId);
var folder = await ProviderInfo.GetFolderAsync(boxFolderId);
return folder;
}
@ -221,50 +221,32 @@ internal abstract class BoxDaoBase : ThirdPartyProviderDao<BoxProviderInfo>
}
}
protected ValueTask<BoxFile> GetBoxFileAsync(string fileId)
public Task<BoxFile> GetFileAsync(string fileId)
{
var boxFileId = MakeBoxId(fileId);
var boxFileId = MakeId(fileId);
try
{
var file = ProviderInfo.GetBoxFileAsync(boxFileId);
var file = ProviderInfo.GetFileAsync(boxFileId);
return file;
}
catch (Exception ex)
{
return ValueTask.FromResult<BoxFile>(new ErrorFile(ex, boxFileId));
return Task.FromResult<BoxFile>(new ErrorFile(ex, boxFileId));
}
}
protected override async Task<IEnumerable<string>> GetChildrenAsync(string folderId)
public override async Task<IEnumerable<string>> GetChildrenAsync(string folderId)
{
var items = await GetBoxItemsAsync(folderId);
var items = await GetItemsAsync(folderId);
return items.Select(entry => MakeId(entry.Id));
}
protected List<BoxItem> GetBoxItems(string parentId, bool? folder = null)
public async Task<List<BoxItem>> GetItemsAsync(string parentId, bool? folder = null)
{
var boxFolderId = MakeBoxId(parentId);
var items = ProviderInfo.GetBoxItemsAsync(boxFolderId).Result;
if (folder.HasValue)
{
if (folder.Value)
{
return items.Where(i => i is BoxFolder).ToList();
}
return items.Where(i => i is BoxFile).ToList();
}
return items;
}
protected async Task<List<BoxItem>> GetBoxItemsAsync(string parentId, bool? folder = null)
{
var boxFolderId = MakeBoxId(parentId);
var items = await ProviderInfo.GetBoxItemsAsync(boxFolderId);
var boxFolderId = MakeId(parentId);
var items = await ProviderInfo.GetItemsAsync(boxFolderId);
if (folder.HasValue)
{
@ -309,36 +291,7 @@ internal abstract class BoxDaoBase : ThirdPartyProviderDao<BoxProviderInfo>
}
}
protected string GetAvailableTitle(string requestTitle, string parentFolderId, Func<string, string, bool> isExist)
{
if (!isExist(requestTitle, parentFolderId))
{
return requestTitle;
}
var re = new Regex(@"( \(((?<index>[0-9])+)\)(\.[^\.]*)?)$");
var match = re.Match(requestTitle);
if (!match.Success)
{
var insertIndex = requestTitle.Length;
if (requestTitle.LastIndexOf('.') != -1)
{
insertIndex = requestTitle.LastIndexOf('.');
}
requestTitle = requestTitle.Insert(insertIndex, " (1)");
}
while (isExist(requestTitle, parentFolderId))
{
requestTitle = re.Replace(requestTitle, MatchEvaluator);
}
return requestTitle;
}
protected async Task<string> GetAvailableTitleAsync(string requestTitle, string parentFolderId, Func<string, string, Task<bool>> isExist)
public async Task<string> GetAvailableTitleAsync(string requestTitle, string parentFolderId, Func<string, string, Task<bool>> isExist)
{
if (!await isExist(requestTitle, parentFolderId))
{

View File

@ -463,7 +463,7 @@ internal class BoxFileDao : BoxDaoBase, IFileDao<string>
throw new Exception(errorFile.Error);
}
var toBoxFolder = await GetBoxFolderAsync(toFolderId);
var toBoxFolder = await GetFolderAsync(toFolderId);
if (toBoxFolder is ErrorFolder errorFolder)
{
throw new Exception(errorFolder.Error);
@ -505,7 +505,7 @@ internal class BoxFileDao : BoxDaoBase, IFileDao<string>
throw new Exception(errorFile.Error);
}
var toBoxFolder = await GetBoxFolderAsync(toFolderId);
var toBoxFolder = await GetFolderAsync(toFolderId);
if (toBoxFolder is ErrorFolder errorFolder)
{
throw new Exception(errorFolder.Error);

View File

@ -62,7 +62,7 @@ internal class BoxFolderDao : BoxDaoBase, IFolderDao<string>
public async Task<Folder<string>> GetFolderAsync(string folderId)
{
return ToFolder(await GetBoxFolderAsync(folderId));
return ToFolder(await base.GetFolderAsync(folderId));
}
public async Task<Folder<string>> GetFolderAsync(string title, string parentId)
@ -178,7 +178,7 @@ internal class BoxFolderDao : BoxDaoBase, IFolderDao<string>
while (folderId != null)
{
var boxFolder = await GetBoxFolderAsync(folderId);
var boxFolder = await base.GetFolderAsync(folderId);
if (boxFolder is ErrorFolder)
{
@ -246,7 +246,7 @@ internal class BoxFolderDao : BoxDaoBase, IFolderDao<string>
public async Task DeleteFolderAsync(string folderId)
{
var boxFolder = await GetBoxFolderAsync(folderId);
var boxFolder = await base.GetFolderAsync(folderId);
var id = MakeId(boxFolder);
using var filesDbContext = _dbContextFactory.CreateDbContext();
@ -310,13 +310,13 @@ internal class BoxFolderDao : BoxDaoBase, IFolderDao<string>
public async Task<string> MoveFolderAsync(string folderId, string toFolderId, CancellationToken? cancellationToken)
{
var boxFolder = await GetBoxFolderAsync(folderId);
var boxFolder = await base.GetFolderAsync(folderId);
if (boxFolder is ErrorFolder errorFolder)
{
throw new Exception(errorFolder.Error);
}
var toBoxFolder = await GetBoxFolderAsync(toFolderId);
var toBoxFolder = await base.GetFolderAsync(toFolderId);
if (toBoxFolder is ErrorFolder errorFolder1)
{
throw new Exception(errorFolder1.Error);
@ -378,13 +378,13 @@ internal class BoxFolderDao : BoxDaoBase, IFolderDao<string>
public async Task<Folder<string>> CopyFolderAsync(string folderId, string toFolderId, CancellationToken? cancellationToken)
{
var boxFolder = await GetBoxFolderAsync(folderId);
var boxFolder = await base.GetFolderAsync(folderId);
if (boxFolder is ErrorFolder errorFolder)
{
throw new Exception(errorFolder.Error);
}
var toBoxFolder = await GetBoxFolderAsync(toFolderId);
var toBoxFolder = await base.GetFolderAsync(toFolderId);
if (toBoxFolder is ErrorFolder errorFolder1)
{
throw new Exception(errorFolder1.Error);
@ -439,7 +439,7 @@ internal class BoxFolderDao : BoxDaoBase, IFolderDao<string>
public async Task<string> RenameFolderAsync(Folder<string> folder, string newTitle)
{
var boxFolder = await GetBoxFolderAsync(folder.Id);
var boxFolder = await base.GetFolderAsync(folder.Id);
var parentFolderId = GetParentFolderId(boxFolder);
if (IsRoot(boxFolder))

View File

@ -28,328 +28,11 @@ namespace ASC.Files.Thirdparty.Box;
[Transient]
[DebuggerDisplay("{CustomerTitle}")]
internal class BoxProviderInfo : IProviderInfo
internal class BoxProviderInfo : AbstractProviderInfo<BoxFile, BoxFolder, BoxItem, BoxLoginProvider>
{
private readonly BoxProviderInfoHelper _boxProviderInfoHelper;
private readonly BoxStorageDisposableWrapper _wrapper;
private string _rootId;
internal override string Selector { get; } = Selectors.Box.Id;
public BoxProviderInfo(
BoxStorageDisposableWrapper wrapper,
BoxProviderInfoHelper boxProviderInfoHelper)
public BoxProviderInfo(DisposableWrapper wrapper) : base(wrapper)
{
_wrapper = wrapper;
_boxProviderInfoHelper = boxProviderInfoHelper;
}
public string BoxRootId
{
get
{
if (string.IsNullOrEmpty(_rootId))
{
var storage = StorageAsync.Result;
_rootId = storage.GetRootFolderIdAsync().Result;
}
return _rootId;
}
}
public DateTime CreateOn { get; set; }
public string CustomerTitle { get; set; }
public string FolderId { get; set; }
public FolderType FolderType { get; set; }
public bool HasLogo { get; set; }
public int ID { get; set; }
public Guid Owner { get; set; }
public bool Private { get; set; }
public string ProviderKey { get; set; }
public string RootFolderId => $"{Selectors.Box.Id}-" + ID;
public FolderType RootFolderType { get; set; }
public OAuth20Token Token { get; set; }
internal bool StorageOpened => _wrapper.TryGetStorage(ID, out var storage) && storage.IsOpened;
internal Task<BoxStorage> StorageAsync
{
get
{
if (!_wrapper.TryGetStorage(ID, out var storage) || !storage.IsOpened)
{
return _wrapper.CreateStorageAsync(Token, ID);
}
return Task.FromResult(storage);
}
}
public Task<bool> CheckAccessAsync()
{
try
{
return Task.FromResult(!string.IsNullOrEmpty(BoxRootId));
}
catch (UnauthorizedAccessException)
{
return Task.FromResult(false);
}
}
public void Dispose()
{
if (StorageOpened)
{
StorageAsync.Result.Close();
}
}
public Task InvalidateStorageAsync()
{
if (_wrapper != null)
{
_wrapper.Dispose();
}
return CacheResetAsync();
}
public void UpdateTitle(string newtitle)
{
CustomerTitle = newtitle;
}
internal Task CacheResetAsync(BoxItem boxItem)
{
return _boxProviderInfoHelper.CacheResetAsync(ID, boxItem);
}
internal Task CacheResetAsync(string boxPath = null, bool? isFile = null)
{
return _boxProviderInfoHelper.CacheResetAsync(BoxRootId, ID, boxPath, isFile);
}
internal async ValueTask<BoxFile> GetBoxFileAsync(string dropboxFilePath)
{
var storage = await StorageAsync;
return await _boxProviderInfoHelper.GetBoxFileAsync(storage, ID, dropboxFilePath);
}
internal async Task<BoxFolder> GetBoxFolderAsync(string dropboxFolderPath)
{
var storage = await StorageAsync;
return await _boxProviderInfoHelper.GetBoxFolderAsync(storage, ID, dropboxFolderPath);
}
internal async Task<List<BoxItem>> GetBoxItemsAsync(string dropboxFolderPath)
{
var storage = await StorageAsync;
return await _boxProviderInfoHelper.GetBoxItemsAsync(storage, ID, dropboxFolderPath);
}
internal async Task<Stream> GetThumbnailAsync(string boxFileId, int width, int height)
{
var storage = await StorageAsync;
return await _boxProviderInfoHelper.GetThumbnailAsync(storage, boxFileId, width, height);
}
}
[Transient]
internal class BoxStorageDisposableWrapper : IDisposable
{
private readonly ConsumerFactory _consumerFactory;
private readonly OAuth20TokenHelper _oAuth20TokenHelper;
private readonly IServiceProvider _serviceProvider;
private readonly TempStream _tempStream;
private readonly ConcurrentDictionary<int, BoxStorage> _storages =
new ConcurrentDictionary<int, BoxStorage>();
public BoxStorageDisposableWrapper(ConsumerFactory consumerFactory, TempStream tempStream, IServiceProvider serviceProvider, OAuth20TokenHelper oAuth20TokenHelper)
{
_consumerFactory = consumerFactory;
_tempStream = tempStream;
_serviceProvider = serviceProvider;
_oAuth20TokenHelper = oAuth20TokenHelper;
}
public void Dispose()
{
foreach (var (key, storage) in _storages)
{
storage.Close();
_storages.Remove(key, out _);
}
}
internal Task<BoxStorage> CreateStorageAsync(OAuth20Token token, int id)
{
if (TryGetStorage(id, out var storage) && storage.IsOpened)
{
return Task.FromResult(storage);
}
return InternalCreateStorageAsync(token, id);
}
internal bool TryGetStorage(int providerId, out BoxStorage storage)
{
return _storages.TryGetValue(providerId, out storage);
}
private Task CheckTokenAsync(OAuth20Token token, int id)
{
if (token == null)
{
throw new UnauthorizedAccessException("Cannot create Box session with given token");
}
return InternalCheckTokenAsync(token, id);
}
private async Task InternalCheckTokenAsync(OAuth20Token token, int id)
{
if (token.IsExpired)
{
token = _oAuth20TokenHelper.RefreshToken<BoxLoginProvider>(_consumerFactory, token);
var dbDao = _serviceProvider.GetService<ProviderAccountDao>();
await dbDao.UpdateProviderInfoAsync(id, new AuthData(token: token.ToJson()));
}
}
private async Task<BoxStorage> InternalCreateStorageAsync(OAuth20Token token, int id)
{
var boxStorage = new BoxStorage(_tempStream);
await CheckTokenAsync(token, id);
boxStorage.Open(token);
_storages.TryAdd(id, boxStorage);
return boxStorage;
}
}
[Singletone]
public class BoxProviderInfoHelper
{
private readonly ICache _cacheChildItems;
private readonly TimeSpan _cacheExpiration = TimeSpan.FromMinutes(1);
private readonly ICache _cacheFile;
private readonly ICache _cacheFolder;
private readonly ICacheNotify<BoxCacheItem> _cacheNotify;
public BoxProviderInfoHelper(ICacheNotify<BoxCacheItem> cacheNotify, ICache cache)
{
_cacheFile = cache;
_cacheFolder = cache;
_cacheChildItems = cache;
_cacheNotify = cacheNotify;
_cacheNotify.Subscribe((i) =>
{
if (i.ResetAll)
{
_cacheChildItems.Remove(new Regex($"^{Selectors.Box.Id}-" + i.Key + ".*"));
_cacheFile.Remove(new Regex($"^{Selectors.Box.Id}f-" + i.Key + ".*"));
_cacheFolder.Remove(new Regex($"^{Selectors.Box.Id}d-" + i.Key + ".*"));
}
if (!i.IsFileExists)
{
_cacheChildItems.Remove($"{Selectors.Box.Id}-" + i.Key);
_cacheFolder.Remove($"{Selectors.Box.Id}d-" + i.Key);
}
else
{
if (i.IsFileExists)
{
_cacheFile.Remove($"{Selectors.Box.Id}f-" + i.Key);
}
else
{
_cacheFolder.Remove($"{Selectors.Box.Id}d-" + i.Key);
}
}
}, CacheNotifyAction.Remove);
}
internal async Task CacheResetAsync(int id, BoxItem boxItem)
{
if (boxItem != null)
{
await _cacheNotify.PublishAsync(new BoxCacheItem { IsFile = boxItem is BoxFile, Key = id + "-" + boxItem.Id }, CacheNotifyAction.Remove);
}
}
internal async Task CacheResetAsync(string boxRootId, int id, string boxId = null, bool? isFile = null)
{
var key = id + "-";
if (boxId == null)
{
await _cacheNotify.PublishAsync(new BoxCacheItem { ResetAll = true, Key = key }, CacheNotifyAction.Remove);
}
else
{
if (boxId == boxRootId)
{
boxId = "0";
}
key += boxId;
await _cacheNotify.PublishAsync(new BoxCacheItem { IsFile = isFile ?? false, IsFileExists = isFile.HasValue, Key = key }, CacheNotifyAction.Remove);
}
}
internal async ValueTask<BoxFile> GetBoxFileAsync(BoxStorage storage, int id, string boxFileId)
{
var file = _cacheFile.Get<BoxFile>($"{Selectors.Box.Id}f-" + id + "-" + boxFileId);
if (file == null)
{
file = await storage.GetFileAsync(boxFileId);
if (file != null)
{
_cacheFile.Insert($"{Selectors.Box.Id}f-" + id + "-" + boxFileId, file, DateTime.UtcNow.Add(_cacheExpiration));
}
}
return file;
}
internal async Task<BoxFolder> GetBoxFolderAsync(BoxStorage storage, int id, string boxFolderId)
{
var folder = _cacheFolder.Get<BoxFolder>($"{Selectors.Box.Id}d-" + id + "-" + boxFolderId);
if (folder == null)
{
folder = await storage.GetFolderAsync(boxFolderId);
if (folder != null)
{
_cacheFolder.Insert($"{Selectors.Box.Id}d-" + id + "-" + boxFolderId, folder, DateTime.UtcNow.Add(_cacheExpiration));
}
}
return folder;
}
internal async Task<List<BoxItem>> GetBoxItemsAsync(BoxStorage storage, int id, string boxFolderId)
{
var items = _cacheChildItems.Get<List<BoxItem>>($"{Selectors.Box.Id}-" + id + "-" + boxFolderId);
if (items == null)
{
items = await storage.GetItemsAsync(boxFolderId);
_cacheChildItems.Insert($"{Selectors.Box.Id}-" + id + "-" + boxFolderId, items, DateTime.UtcNow.Add(_cacheExpiration));
}
return items;
}
internal async Task<Stream> GetThumbnailAsync(BoxStorage storage, string boxFileId, int width, int height)
{
return await storage.GetThumbnailAsync(boxFileId, width, height);
}
}

View File

@ -24,11 +24,13 @@
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
using Microsoft.IdentityModel.Tokens;
using BoxSDK = Box.V2;
namespace ASC.Files.Thirdparty.Box;
internal class BoxStorage
internal class BoxStorage : IThirdPartyStorage<BoxFile, BoxFolder, BoxItem>
{
private BoxClient _boxClient;
@ -37,7 +39,7 @@ internal class BoxStorage
public bool IsOpened { get; private set; }
private readonly TempStream _tempStream;
public long MaxChunkedUploadFileSize = 250L * 1024L * 1024L;
private readonly long _maxChunkedUploadFileSize = 250L * 1024L * 1024L;
public BoxStorage(TempStream tempStream)
{
@ -63,13 +65,6 @@ internal class BoxStorage
IsOpened = false;
}
public async Task<string> GetRootFolderIdAsync()
{
var root = await GetFolderAsync("0");
return root.Id;
}
public async Task<BoxFolder> GetFolderAsync(string folderId)
{
try
@ -87,25 +82,38 @@ internal class BoxStorage
}
}
public ValueTask<BoxFile> GetFileAsync(string fileId)
public async Task<bool> CheckAccessAsync()
{
try
{
return new ValueTask<BoxFile>(_boxClient.FilesManager.GetInformationAsync(fileId, _boxFields));
var rootFolder = await GetFolderAsync("0");
return !string.IsNullOrEmpty(rootFolder.Id);
}
catch (UnauthorizedAccessException)
{
return false;
}
}
public Task<BoxFile> GetFileAsync(string fileId)
{
try
{
return _boxClient.FilesManager.GetInformationAsync(fileId, _boxFields);
}
catch (Exception ex)
{
if (ex.InnerException is BoxSDK.Exceptions.BoxAPIException boxException && boxException.Error.Status == ((int)HttpStatusCode.NotFound).ToString())
{
return ValueTask.FromResult<BoxFile>(null);
return Task.FromResult<BoxFile>(null);
}
throw;
}
}
public async Task<List<BoxItem>> GetItemsAsync(string folderId, int limit = 500)
public async Task<List<BoxItem>> GetItemsAsync(string folderId)
{
var folderItems = await _boxClient.FoldersManager.GetFolderItemsAsync(folderId, limit, 0, _boxFields);
var folderItems = await _boxClient.FoldersManager.GetFolderItemsAsync(folderId, 500, 0, _boxFields);
return folderItems.Entries;
}
@ -117,7 +125,7 @@ internal class BoxStorage
return InternalDownloadStreamAsync(file, offset);
}
public async Task<Stream> InternalDownloadStreamAsync(BoxFile file, int offset = 0)
private async Task<Stream> InternalDownloadStreamAsync(BoxFile file, int offset = 0)
{
if (offset > 0 && file.Size.HasValue)
{
@ -265,15 +273,14 @@ internal class BoxStorage
public async Task<long> GetMaxUploadSizeAsync()
{
var boxUser = await _boxClient.UsersManager.GetCurrentUserInformationAsync(new List<string>() { "max_upload_size" });
var max = boxUser.MaxUploadSize ?? MaxChunkedUploadFileSize;
var max = boxUser.MaxUploadSize ?? _maxChunkedUploadFileSize;
//todo: without chunked uploader:
return Math.Min(max, MaxChunkedUploadFileSize);
return Math.Min(max, _maxChunkedUploadFileSize);
}
public async Task<Stream> GetThumbnailAsync(string fileId, int width, int height)
{
var boxRepresentation = new BoxRepresentationRequest();
boxRepresentation.FileId = fileId;
boxRepresentation.XRepHints = $"[jpg?dimensions=320x320]";

View File

@ -26,315 +26,13 @@
namespace ASC.Files.Thirdparty.Dropbox;
[Singletone]
public class DropboxProviderInfoHelper
{
private readonly ICache _cacheChildItems;
private readonly TimeSpan _cacheExpiration;
private readonly ICache _cacheFile;
private readonly ICache _cacheFolder;
private readonly ICacheNotify<DropboxCacheItem> _cacheNotify;
public DropboxProviderInfoHelper(ICacheNotify<DropboxCacheItem> cacheNotify, ICache cache)
{
_cacheExpiration = TimeSpan.FromMinutes(1);
_cacheFile = cache;
_cacheFolder = cache;
_cacheChildItems = cache;
_cacheNotify = cacheNotify;
_cacheNotify.Subscribe((i) =>
{
if (i.ResetAll)
{
_cacheFile.Remove(new Regex($"^{Selectors.Dropbox.Id}f-" + i.Key + ".*"));
_cacheFolder.Remove(new Regex($"^{Selectors.Dropbox.Id}d-" + i.Key + ".*"));
_cacheChildItems.Remove(new Regex($"^{Selectors.Dropbox.Id}-" + i.Key + ".*"));
}
if (!i.IsFileExists)
{
_cacheChildItems.Remove($"{Selectors.Dropbox.Id}-" + i.Key);
_cacheFolder.Remove($"{Selectors.Dropbox.Id}d-" + i.Key);
}
else
{
if (i.IsFileExists)
{
_cacheFile.Remove($"{Selectors.Dropbox.Id}f-" + i.Key);
}
else
{
_cacheFolder.Remove($"{Selectors.Dropbox.Id}d-" + i.Key);
}
}
}, CacheNotifyAction.Remove);
}
internal async Task CacheResetAsync(int id, Metadata dropboxItem)
{
if (dropboxItem != null)
{
await _cacheNotify.PublishAsync(new DropboxCacheItem { IsFile = dropboxItem.AsFolder != null, Key = id + "-" + dropboxItem.PathDisplay }, CacheNotifyAction.Remove);
}
}
internal async Task CacheResetAsync(int id, string dropboxPath = null, bool? isFile = null)
{
var key = id + "-";
if (dropboxPath == null)
{
await _cacheNotify.PublishAsync(new DropboxCacheItem { ResetAll = true, Key = key }, CacheNotifyAction.Remove);
}
else
{
key += dropboxPath;
await _cacheNotify.PublishAsync(new DropboxCacheItem { IsFile = isFile ?? false, IsFileExists = isFile.HasValue, Key = key }, CacheNotifyAction.Remove);
}
}
internal async ValueTask<FileMetadata> GetDropboxFileAsync(DropboxStorage storage, int id, string dropboxFilePath)
{
var file = _cacheFile.Get<FileMetadata>($"{Selectors.Dropbox.Id}f-" + id + "-" + dropboxFilePath);
if (file == null)
{
file = await storage.GetFileAsync(dropboxFilePath);
if (file != null)
{
_cacheFile.Insert($"{Selectors.Dropbox.Id}f-" + id + "-" + dropboxFilePath, file, DateTime.UtcNow.Add(_cacheExpiration));
}
}
return file;
}
internal async Task<FolderMetadata> GetDropboxFolderAsync(DropboxStorage storage, int id, string dropboxFolderPath)
{
var folder = _cacheFolder.Get<FolderMetadata>($"{Selectors.Dropbox.Id}d-" + id + "-" + dropboxFolderPath);
if (folder == null)
{
folder = await storage.GetFolderAsync(dropboxFolderPath);
if (folder != null)
{
_cacheFolder.Insert($"{Selectors.Dropbox.Id}d-" + id + "-" + dropboxFolderPath, folder, DateTime.UtcNow.Add(_cacheExpiration));
}
}
return folder;
}
internal async Task<List<Metadata>> GetDropboxItemsAsync(DropboxStorage storage, int id, string dropboxFolderPath)
{
var items = _cacheChildItems.Get<List<Metadata>>($"{Selectors.Dropbox.Id}-" + id + "-" + dropboxFolderPath);
if (items == null)
{
items = await storage.GetItemsAsync(dropboxFolderPath);
_cacheChildItems.Insert($"{Selectors.Dropbox.Id}-" + id + "-" + dropboxFolderPath, items, DateTime.UtcNow.Add(_cacheExpiration));
}
return items;
}
internal Task<Stream> GetThumbnailsAsync(DropboxStorage storage, string filePath, int width, int height)
{
return storage.GetThumbnailsAsync(filePath, width, height);
}
}
[Transient]
[DebuggerDisplay("{CustomerTitle}")]
internal class DropboxProviderInfo : IProviderInfo
internal class DropboxProviderInfo : AbstractProviderInfo<FileMetadata, FolderMetadata, Metadata, DropboxLoginProvider>
{
private readonly DropboxProviderInfoHelper _dropboxProviderInfoHelper;
private readonly DropboxStorageDisposableWrapper _wrapper;
internal override string Selector { get; } = Selectors.Dropbox.Id;
public DropboxProviderInfo(
DropboxStorageDisposableWrapper wrapper,
DropboxProviderInfoHelper dropboxProviderInfoHelper
)
public DropboxProviderInfo(DisposableWrapper wrapper) : base(wrapper)
{
_wrapper = wrapper;
_dropboxProviderInfoHelper = dropboxProviderInfoHelper;
}
public DateTime CreateOn { get; set; }
public string CustomerTitle { get; set; }
public string FolderId { get; set; }
public FolderType FolderType { get; set; }
public bool HasLogo { get; set; }
public int ID { get; set; }
public Guid Owner { get; set; }
public bool Private { get; set; }
public string ProviderKey { get; set; }
public string RootFolderId => $"{Selectors.Dropbox.Id}-" + ID;
public FolderType RootFolderType { get; set; }
public OAuth20Token Token { get; set; }
internal bool StorageOpened => _wrapper.TryGetStorage(ID, out var storage) && storage.IsOpened;
internal Task<DropboxStorage> StorageAsync
{
get
{
if (!_wrapper.TryGetStorage(ID, out var storage) || !storage.IsOpened)
{
return _wrapper.CreateStorageAsync(Token, ID);
}
return Task.FromResult(storage);
}
}
public async Task<bool> CheckAccessAsync()
{
try
{
await (await StorageAsync).GetUsedSpaceAsync();
}
catch (AggregateException)
{
return false;
}
return true;
}
public void Dispose()
{
if (StorageOpened)
{
StorageAsync.Result.Close();
}
}
public Task InvalidateStorageAsync()
{
if (_wrapper != null)
{
_wrapper.Dispose();
}
return CacheResetAsync();
}
public void UpdateTitle(string newtitle)
{
CustomerTitle = newtitle;
}
internal Task CacheResetAsync(Metadata dropboxItem)
{
return _dropboxProviderInfoHelper.CacheResetAsync(ID, dropboxItem);
}
internal Task CacheResetAsync(string dropboxPath = null, bool? isFile = null)
{
return _dropboxProviderInfoHelper.CacheResetAsync(ID, dropboxPath, isFile);
}
internal async Task<FileMetadata> GetDropboxFileAsync(string dropboxFilePath)
{
var storage = await StorageAsync;
return await _dropboxProviderInfoHelper.GetDropboxFileAsync(storage, ID, dropboxFilePath);
}
internal async Task<FolderMetadata> GetDropboxFolderAsync(string dropboxFolderPath)
{
var storage = await StorageAsync;
return await _dropboxProviderInfoHelper.GetDropboxFolderAsync(storage, ID, dropboxFolderPath);
}
internal async Task<List<Metadata>> GetDropboxItemsAsync(string dropboxFolderPath)
{
var storage = await StorageAsync;
return await _dropboxProviderInfoHelper.GetDropboxItemsAsync(storage, ID, dropboxFolderPath);
}
internal async Task<Stream> GetThumbnailsAsync(string filePath, int width, int height)
{
var storage = await StorageAsync;
return await _dropboxProviderInfoHelper.GetThumbnailsAsync(storage, filePath, width, height);
}
}
[Transient(Additional = typeof(DropboxStorageDisposableWrapperExtention))]
internal class DropboxStorageDisposableWrapper : IDisposable
{
private readonly ConsumerFactory _consumerFactory;
private readonly OAuth20TokenHelper _oAuth20TokenHelper;
private readonly IServiceProvider _serviceProvider;
private readonly ConcurrentDictionary<int, DropboxStorage> _storages =
new ConcurrentDictionary<int, DropboxStorage>();
public DropboxStorageDisposableWrapper(IServiceProvider serviceProvider, OAuth20TokenHelper oAuth20TokenHelper, ConsumerFactory consumerFactory)
{
_serviceProvider = serviceProvider;
_oAuth20TokenHelper = oAuth20TokenHelper;
_consumerFactory = consumerFactory;
}
public Task<DropboxStorage> CreateStorageAsync(OAuth20Token token, int id)
{
if (TryGetStorage(id, out var storage) && storage.IsOpened)
{
return Task.FromResult(storage);
}
return InternalCreateStorageAsync(token, id);
}
public bool TryGetStorage(int id, out DropboxStorage storage)
{
return _storages.TryGetValue(id, out storage);
}
public void Dispose()
{
foreach (var (key, storage) in _storages)
{
storage.Close();
_storages.Remove(key, out _);
}
}
public async Task<DropboxStorage> InternalCreateStorageAsync(OAuth20Token token, int id)
{
var dropboxStorage = _serviceProvider.GetRequiredService<DropboxStorage>();
await CheckTokenAsync(token, id);
dropboxStorage.Open(token);
_storages.TryAdd(id, dropboxStorage);
return dropboxStorage;
}
private Task CheckTokenAsync(OAuth20Token token, int id)
{
if (token == null)
{
throw new UnauthorizedAccessException("Cannot create Dropbox session with given token");
}
return InternalCheckTokenAsync(token, id);
}
private async Task InternalCheckTokenAsync(OAuth20Token token, int id)
{
if (token.IsExpired)
{
token = _oAuth20TokenHelper.RefreshToken<DropboxLoginProvider>(_consumerFactory, token);
var dbDao = _serviceProvider.GetService<ProviderAccountDao>();
var authData = new AuthData(token: token.ToJson());
await dbDao.UpdateProviderInfoAsync(id, authData);
}
}
}
public static class DropboxStorageDisposableWrapperExtention
{
public static void Register(DIHelper dIHelper)
{
dIHelper.TryAdd<DropboxStorage>();
}
}

View File

@ -24,12 +24,14 @@
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
using Google.Api.Gax.ResourceNames;
using ThumbnailSize = Dropbox.Api.Files.ThumbnailSize;
namespace ASC.Files.Thirdparty.Dropbox;
[Transient]
internal class DropboxStorage : IDisposable
internal class DropboxStorage : IThirdPartyStorage<FileMetadata, FolderMetadata, Metadata>, IDisposable
{
public bool IsOpened { get; private set; }
public long MaxChunkedUploadFileSize = 20L * 1024L * 1024L * 1024L;
@ -68,11 +70,17 @@ internal class DropboxStorage : IDisposable
return (parentPath ?? "") + "/" + (name ?? "");
}
public async Task<long> GetUsedSpaceAsync()
public async Task<bool> CheckAccessAsync()
{
var spaceUsage = await _dropboxClient.Users.GetSpaceUsageAsync();
return (long)spaceUsage.Used;
try
{
await _dropboxClient.Users.GetSpaceUsageAsync();
return true;
}
catch (AggregateException)
{
return false;
}
}
@ -86,7 +94,7 @@ internal class DropboxStorage : IDisposable
return InternalGetFolderAsync(folderPath);
}
public async Task<FolderMetadata> InternalGetFolderAsync(string folderPath)
private async Task<FolderMetadata> InternalGetFolderAsync(string folderPath)
{
try
{
@ -105,17 +113,17 @@ internal class DropboxStorage : IDisposable
}
}
public ValueTask<FileMetadata> GetFileAsync(string filePath)
public Task<FileMetadata> GetFileAsync(string filePath)
{
if (string.IsNullOrEmpty(filePath) || filePath == "/")
{
return ValueTask.FromResult<FileMetadata>(null);
return Task.FromResult<FileMetadata>(null);
}
return InternalGetFileAsync(filePath);
}
private async ValueTask<FileMetadata> InternalGetFileAsync(string filePath)
private async Task<FileMetadata> InternalGetFileAsync(string filePath)
{
try
{
@ -141,7 +149,7 @@ internal class DropboxStorage : IDisposable
return new List<Metadata>(data.Entries);
}
public async Task<Stream> GetThumbnailsAsync(string filePath, int width, int height)
public async Task<Stream> GetThumbnailAsync(string filePath, int width, int height)
{
try
{
@ -170,14 +178,15 @@ internal class DropboxStorage : IDisposable
}
}
public Task<Stream> DownloadStreamAsync(string filePath, int offset = 0)
public Task<Stream> DownloadStreamAsync(FileMetadata file, int offset = 0)
{
var filePath = MakeId(file);
ArgumentNullOrEmptyException.ThrowIfNullOrEmpty(filePath);
return InternalDownloadStreamAsync(filePath, offset);
}
public async Task<Stream> InternalDownloadStreamAsync(string filePath, int offset = 0)
private async Task<Stream> InternalDownloadStreamAsync(string filePath, int offset = 0)
{
using var response = await _dropboxClient.Files.DownloadAsync(filePath);
var tempBuffer = _tempStream.Create();
@ -222,6 +231,15 @@ internal class DropboxStorage : IDisposable
return (FolderMetadata)result.Metadata;
}
public async Task<FolderMetadata> RenameFolderAsync(string dropboxFolderPath, string folderName)
{
var folder = await GetFolderAsync(dropboxFolderPath);
var pathTo = GetParentFolderId(folder);
var result = await _dropboxClient.Files.MoveV2Async(dropboxFolderPath, MakeDropboxPath(pathTo, folderName), autorename: true);
return (FolderMetadata)result.Metadata;
}
public async Task<FileMetadata> MoveFileAsync(string dropboxFilePath, string dropboxFolderPathTo, string fileName)
{
var pathTo = MakeDropboxPath(dropboxFolderPathTo, fileName);
@ -230,6 +248,15 @@ internal class DropboxStorage : IDisposable
return (FileMetadata)result.Metadata;
}
public async Task<FileMetadata> RenameFileAsync(string dropboxFilePath, string fileName)
{
var file = await GetFileAsync(dropboxFilePath);
var pathTo = GetParentFolderId(file);
var result = await _dropboxClient.Files.MoveV2Async(dropboxFilePath, MakeDropboxPath(pathTo, fileName), autorename: true);
return (FileMetadata)result.Metadata;
}
public async Task<FolderMetadata> CopyFolderAsync(string dropboxFolderPath, string dropboxFolderPathTo, string folderName)
{
var pathTo = MakeDropboxPath(dropboxFolderPathTo, folderName);
@ -278,6 +305,40 @@ internal class DropboxStorage : IDisposable
new CommitInfo(dropboxFilePath, WriteMode.Overwrite.Instance));
}
public string MakeId(Metadata dropboxItem)
{
string path = null;
if (dropboxItem != null)
{
path = dropboxItem.PathDisplay;
}
return path;
}
public string GetParentFolderId(Metadata dropboxItem)
{
if (dropboxItem == null || IsRoot(dropboxItem.AsFolder))
{
return null;
}
var pathLength = dropboxItem.PathDisplay.Length - dropboxItem.Name.Length;
return dropboxItem.PathDisplay.Substring(0, pathLength > 1 ? pathLength - 1 : 0);
}
public bool IsRoot(FolderMetadata dropboxFolder)
{
return dropboxFolder != null && dropboxFolder.Id == "/";
}
public Task<long> GetMaxUploadSizeAsync()
{
return Task.FromResult(MaxChunkedUploadFileSize);
}
public void Dispose()
{
if (_dropboxClient != null)

View File

@ -28,375 +28,13 @@ using DriveFile = Google.Apis.Drive.v3.Data.File;
namespace ASC.Files.Thirdparty.GoogleDrive;
[Singletone]
public class GoogleDriveProviderInfoHelper
{
private readonly ICache _cacheChildFiles;
private readonly ICache _cacheChildFolders;
private readonly ICache _cacheEntry;
private readonly TimeSpan _cacheExpiration;
private readonly ICacheNotify<GoogleDriveCacheItem> _cacheNotify;
public GoogleDriveProviderInfoHelper(ICacheNotify<GoogleDriveCacheItem> cacheNotify, ICache cache)
{
_cacheExpiration = TimeSpan.FromMinutes(1);
_cacheEntry = cache;
_cacheChildFiles = cache;
_cacheChildFolders = cache;
_cacheNotify = cacheNotify;
_cacheNotify.Subscribe((i) =>
{
if (i.ResetEntry)
{
_cacheEntry.Remove($"{Selectors.GoogleDrive.Id}-" + i.Key);
}
if (i.ResetAll)
{
_cacheEntry.Remove(new Regex($"^{Selectors.GoogleDrive.Id}-" + i.Key + ".*"));
_cacheChildFiles.Remove(new Regex($"^{Selectors.GoogleDrive.Id}f-" + i.Key + ".*"));
_cacheChildFolders.Remove(new Regex($"^{Selectors.GoogleDrive.Id}d-" + i.Key + ".*"));
}
if (i.ResetChilds)
{
if (!i.ChildFolderExist || !i.ChildFolder)
{
_cacheChildFiles.Remove($"{Selectors.GoogleDrive.Id}f-" + i.Key);
}
if (!i.ChildFolderExist || i.ChildFolder)
{
_cacheChildFolders.Remove($"{Selectors.GoogleDrive.Id}d-" + i.Key);
}
}
}, CacheNotifyAction.Remove);
}
internal async Task CacheResetAsync(DriveFile driveEntry, int id)
{
if (driveEntry != null)
{
await _cacheNotify.PublishAsync(new GoogleDriveCacheItem { ResetEntry = true, Key = id + "-" + driveEntry.Id }, CacheNotifyAction.Remove);
}
}
internal async Task CacheResetAsync(string driveRootId, int id, string driveId = null, bool? childFolder = null)
{
var key = id + "-";
if (driveId == null)
{
await _cacheNotify.PublishAsync(new GoogleDriveCacheItem { ResetAll = true, Key = key }, CacheNotifyAction.Remove);
}
else
{
if (driveId == driveRootId)
{
driveId = "root";
}
key += driveId;
await _cacheNotify.PublishAsync(new GoogleDriveCacheItem { ResetEntry = true, ResetChilds = true, Key = key, ChildFolder = childFolder ?? false, ChildFolderExist = childFolder.HasValue }, CacheNotifyAction.Remove);
}
}
internal Task CacheResetChildsAsync(int id, string parentDriveId, bool? childFolder = null)
{
return _cacheNotify.PublishAsync(new GoogleDriveCacheItem { ResetChilds = true, Key = id + "-" + parentDriveId, ChildFolder = childFolder ?? false, ChildFolderExist = childFolder.HasValue }, CacheNotifyAction.Remove);
}
internal async Task<List<DriveFile>> GetDriveEntriesAsync(GoogleDriveStorage storage, int id, string parentDriveId, bool? folder = null)
{
if (folder.HasValue)
{
if (folder.Value)
{
var value = _cacheChildFolders.Get<List<DriveFile>>($"{Selectors.GoogleDrive.Id}d-" + id + "-" + parentDriveId);
if (value == null)
{
value = await storage.GetEntriesAsync(parentDriveId, true);
if (value != null)
{
_cacheChildFolders.Insert($"{Selectors.GoogleDrive.Id}d-" + id + "-" + parentDriveId, value, DateTime.UtcNow.Add(_cacheExpiration));
}
}
return value;
}
else
{
var value = _cacheChildFiles.Get<List<DriveFile>>($"{Selectors.GoogleDrive.Id}f-" + id + "-" + parentDriveId);
if (value == null)
{
value = await storage.GetEntriesAsync(parentDriveId, false);
if (value != null)
{
_cacheChildFiles.Insert($"{Selectors.GoogleDrive.Id}f-" + id + "-" + parentDriveId, value, DateTime.UtcNow.Add(_cacheExpiration));
}
}
return value;
}
}
if (_cacheChildFiles.Get<List<DriveFile>>($"{Selectors.GoogleDrive.Id}f-" + id + "-" + parentDriveId) == null &&
_cacheChildFolders.Get<List<DriveFile>>($"{Selectors.GoogleDrive.Id}d-" + id + "-" + parentDriveId) == null)
{
var entries = await storage.GetEntriesAsync(parentDriveId);
_cacheChildFiles.Insert($"{Selectors.GoogleDrive.Id}f-" + id + "-" + parentDriveId, entries.Where(entry => entry.MimeType != GoogleLoginProvider.GoogleDriveMimeTypeFolder).ToList(), DateTime.UtcNow.Add(_cacheExpiration));
_cacheChildFolders.Insert($"{Selectors.GoogleDrive.Id}d-" + id + "-" + parentDriveId, entries.Where(entry => entry.MimeType == GoogleLoginProvider.GoogleDriveMimeTypeFolder).ToList(), DateTime.UtcNow.Add(_cacheExpiration));
return entries;
}
var folders = _cacheChildFolders.Get<List<DriveFile>>($"{Selectors.GoogleDrive.Id}d-" + id + "-" + parentDriveId);
if (folders == null)
{
folders = await storage.GetEntriesAsync(parentDriveId, true);
_cacheChildFolders.Insert($"{Selectors.GoogleDrive.Id}d-" + id + "-" + parentDriveId, folders, DateTime.UtcNow.Add(_cacheExpiration));
}
var files = _cacheChildFiles.Get<List<DriveFile>>($"{Selectors.GoogleDrive.Id}f-" + id + "-" + parentDriveId);
if (files == null)
{
files = await storage.GetEntriesAsync(parentDriveId, false);
_cacheChildFiles.Insert($"{Selectors.GoogleDrive.Id}f-" + id + "-" + parentDriveId, files, DateTime.UtcNow.Add(_cacheExpiration));
}
return folders.Concat(files).ToList();
}
internal async Task<DriveFile> GetDriveEntryAsync(GoogleDriveStorage storage, int id, string driveId)
{
var entry = _cacheEntry.Get<DriveFile>($"{Selectors.GoogleDrive.Id}-" + id + "-" + driveId);
if (entry == null)
{
entry = await storage.GetEntryAsync(driveId);
if (entry != null)
{
_cacheEntry.Insert($"{Selectors.GoogleDrive.Id}-" + id + "-" + driveId, entry, DateTime.UtcNow.Add(_cacheExpiration));
}
}
return entry;
}
}
[Transient]
[DebuggerDisplay("{CustomerTitle}")]
internal class GoogleDriveProviderInfo : IProviderInfo
internal class GoogleDriveProviderInfo : AbstractProviderInfo<DriveFile, DriveFile, DriveFile, GoogleLoginProvider>
{
private readonly GoogleDriveProviderInfoHelper _googleDriveProviderInfoHelper;
private readonly ILogger _logger;
private readonly GoogleDriveStorageDisposableWrapper _wrapper;
private string _driveRootId;
public GoogleDriveProviderInfo(
GoogleDriveStorageDisposableWrapper storageDisposableWrapper,
GoogleDriveProviderInfoHelper googleDriveProviderInfoHelper,
ILoggerProvider options)
internal override string Selector { get; } = Selectors.GoogleDrive.Id;
public GoogleDriveProviderInfo(DisposableWrapper wrapper) : base(wrapper)
{
_wrapper = storageDisposableWrapper;
_googleDriveProviderInfoHelper = googleDriveProviderInfoHelper;
_logger = options.CreateLogger("ASC.Files");
}
public DateTime CreateOn { get; set; }
public string CustomerTitle { get; set; }
public string FolderId { get; set; }
public FolderType FolderType { get; set; }
public bool HasLogo { get; set; }
public int ID { get; set; }
public Guid Owner { get; set; }
public bool Private { get; set; }
public string ProviderKey { get; set; }
public string RootFolderId => $"{Selectors.GoogleDrive.Id}-" + ID;
public FolderType RootFolderType { get; set; }
public OAuth20Token Token { get; set; }
internal bool StorageOpened => _wrapper.TryGetStorage(ID, out var storage) && storage.IsOpened;
public string DriveRootId
{
get
{
if (string.IsNullOrEmpty(_driveRootId))
{
try
{
_driveRootId = StorageAsync.Result.GetRootFolderId();
}
catch (Exception ex)
{
_logger.ErrorGoogleDrive(ex);
return null;
}
}
return _driveRootId;
}
}
internal Task<GoogleDriveStorage> StorageAsync
{
get
{
if (!_wrapper.TryGetStorage(ID, out var storage) || !storage.IsOpened)
{
return _wrapper.CreateStorageAsync(Token, ID);
}
return Task.FromResult(storage);
}
}
public Task<bool> CheckAccessAsync()
{
try
{
return Task.FromResult(!string.IsNullOrEmpty(DriveRootId));
}
catch (UnauthorizedAccessException)
{
return Task.FromResult(false);
}
}
public void Dispose()
{
if (StorageOpened)
{
StorageAsync.Result.Close();
}
}
public Task InvalidateStorageAsync()
{
if (_wrapper != null)
{
_wrapper.Dispose();
}
return CacheResetAsync();
}
public void UpdateTitle(string newtitle)
{
CustomerTitle = newtitle;
}
internal Task CacheResetAsync(DriveFile driveEntry)
{
return _googleDriveProviderInfoHelper.CacheResetAsync(driveEntry, ID);
}
internal Task CacheResetAsync(string driveId = null, bool? childFolder = null)
{
return _googleDriveProviderInfoHelper.CacheResetAsync(DriveRootId, ID, driveId, childFolder);
}
internal Task CacheResetChildsAsync(string parentDriveId, bool? childFolder = null)
{
return _googleDriveProviderInfoHelper.CacheResetChildsAsync(ID, parentDriveId, childFolder);
}
internal async Task<List<DriveFile>> GetDriveEntriesAsync(string parentDriveId, bool? folder = null)
{
var storage = await StorageAsync;
return await _googleDriveProviderInfoHelper.GetDriveEntriesAsync(storage, ID, parentDriveId, folder);
}
internal async Task<DriveFile> GetDriveEntryAsync(string driveId)
{
var storage = await StorageAsync;
return await _googleDriveProviderInfoHelper.GetDriveEntryAsync(storage, ID, driveId);
}
internal async Task<Stream> GetThumbnail(string fileId, int width, int height)
{
var storage = await StorageAsync;
return await storage.GetThumbnail(fileId, width, height);
}
}
[Transient(Additional = typeof(GoogleDriveProviderInfoExtention))]
internal class GoogleDriveStorageDisposableWrapper : IDisposable
{
private readonly ConsumerFactory _consumerFactory;
private readonly OAuth20TokenHelper _oAuth20TokenHelper;
private readonly IServiceProvider _serviceProvider;
private readonly ConcurrentDictionary<int, GoogleDriveStorage> _storages =
new ConcurrentDictionary<int, GoogleDriveStorage>();
public GoogleDriveStorageDisposableWrapper(ConsumerFactory consumerFactory, IServiceProvider serviceProvider, OAuth20TokenHelper oAuth20TokenHelper)
{
_consumerFactory = consumerFactory;
_serviceProvider = serviceProvider;
_oAuth20TokenHelper = oAuth20TokenHelper;
}
public Task<GoogleDriveStorage> CreateStorageAsync(OAuth20Token token, int id)
{
if (TryGetStorage(id, out var storage) && storage.IsOpened)
{
return Task.FromResult(storage);
}
return InternalCreateStorageAsync(token, id);
}
public bool TryGetStorage(int id, out GoogleDriveStorage storage)
{
return _storages.TryGetValue(id, out storage);
}
public void Dispose()
{
foreach (var (key, storage) in _storages)
{
storage.Close();
_storages.Remove(key, out _);
}
}
public async Task<GoogleDriveStorage> InternalCreateStorageAsync(OAuth20Token token, int id)
{
var driveStorage = _serviceProvider.GetRequiredService<GoogleDriveStorage>();
await CheckTokenAsync(token, id);
driveStorage.Open(token);
_storages.TryAdd(id, driveStorage);
return driveStorage;
}
private Task CheckTokenAsync(OAuth20Token token, int id)
{
if (token == null)
{
throw new UnauthorizedAccessException("Cannot create GoogleDrive session with given token");
}
return InternalCheckTokenAsync(token, id);
}
private async Task InternalCheckTokenAsync(OAuth20Token token, int id)
{
if (token.IsExpired)
{
token = _oAuth20TokenHelper.RefreshToken<GoogleLoginProvider>(_consumerFactory, token);
var dbDao = _serviceProvider.GetService<ProviderAccountDao>();
var authData = new AuthData(token: token.ToJson());
await dbDao.UpdateProviderInfoAsync(id, authData);
}
}
}
public static class GoogleDriveProviderInfoExtention
{
public static void Register(DIHelper dIHelper)
{
dIHelper.TryAdd<GoogleDriveStorage>();
}
}

View File

@ -24,13 +24,15 @@
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
using System.IO;
using DriveFile = Google.Apis.Drive.v3.Data.File;
using MimeMapping = ASC.Common.Web.MimeMapping;
namespace ASC.Files.Thirdparty.GoogleDrive;
[Transient]
internal class GoogleDriveStorage : IDisposable
internal class GoogleDriveStorage : IThirdPartyStorage<DriveFile, DriveFile, DriveFile>, IDisposable
{
private OAuth20Token _token;
@ -130,31 +132,11 @@ internal class GoogleDriveStorage : IDisposable
return rootFolder.Id;
}
public DriveFile GetEntry(string entryId)
public async Task<DriveFile> GetItemAsync(string itemId)
{
try
{
var request = _driveService.Files.Get(entryId);
request.Fields = GoogleLoginProvider.FilesFields;
return request.Execute();
}
catch (GoogleApiException ex)
{
if (ex.HttpStatusCode == HttpStatusCode.NotFound)
{
return null;
}
throw;
}
}
public async Task<DriveFile> GetEntryAsync(string entryId)
{
try
{
var request = _driveService.Files.Get(entryId);
var request = _driveService.Files.Get(itemId);
request.Fields = GoogleLoginProvider.FilesFields;
return await request.ExecuteAsync();
@ -169,7 +151,7 @@ internal class GoogleDriveStorage : IDisposable
}
}
public async Task<Stream> GetThumbnail(string fileId, int width, int height)
public async Task<Stream> GetThumbnailAsync(string fileId, int width, int height)
{
try
{
@ -184,42 +166,12 @@ internal class GoogleDriveStorage : IDisposable
}
}
public List<DriveFile> GetEntries(string folderId, bool? folders = null)
public Task<List<DriveFile>> GetItemsAsync(string folderId)
{
var request = _driveService.Files.List();
var query = "'" + folderId + "' in parents and trashed=false";
if (folders.HasValue)
{
query += " and mimeType " + (folders.Value ? "" : "!") + "= '" + GoogleLoginProvider.GoogleDriveMimeTypeFolder + "'";
}
request.Q = query;
request.Fields = "nextPageToken, files(" + GoogleLoginProvider.FilesFields + ")";
var files = new List<DriveFile>();
do
{
try
{
var fileList = request.Execute();
files.AddRange(fileList.Files);
request.PageToken = fileList.NextPageToken;
}
catch (Exception)
{
request.PageToken = null;
}
} while (!string.IsNullOrEmpty(request.PageToken));
return files;
return GetItemsInternalAsync(folderId);
}
public async Task<List<DriveFile>> GetEntriesAsync(string folderId, bool? folders = null)
private async Task<List<DriveFile>> GetItemsInternalAsync(string folderId, bool? folders = null)
{
var request = _driveService.Files.List();
@ -303,37 +255,6 @@ internal class GoogleDriveStorage : IDisposable
return tempBuffer;
}
public DriveFile InsertEntry(Stream fileStream, string title, string parentId, bool folder = false)
{
var mimeType = folder ? GoogleLoginProvider.GoogleDriveMimeTypeFolder : MimeMapping.GetMimeMapping(title);
var body = FileConstructor(title, mimeType, parentId);
if (folder)
{
var requestFolder = _driveService.Files.Create(body);
requestFolder.Fields = GoogleLoginProvider.FilesFields;
return requestFolder.Execute();
}
var request = _driveService.Files.Create(body, fileStream, mimeType);
request.Fields = GoogleLoginProvider.FilesFields;
var result = request.Upload();
if (result.Exception != null)
{
if (request.ResponseBody == null)
{
throw result.Exception;
}
_logger.ErrorWhileTryingToInsertEntity(result.Exception);
}
return request.ResponseBody;
}
public async Task<DriveFile> InsertEntryAsync(Stream fileStream, string title, string parentId, bool folder = false)
{
var mimeType = folder ? GoogleLoginProvider.GoogleDriveMimeTypeFolder : MimeMapping.GetMimeMapping(title);
@ -364,15 +285,9 @@ internal class GoogleDriveStorage : IDisposable
return request.ResponseBody;
}
public void DeleteEntry(string entryId)
public Task DeleteItemAsync(DriveFile entry)
{
_driveService.Files.Delete(entryId).Execute();
}
public Task DeleteEntryAsync(string entryId)
{
return _driveService.Files.Delete(entryId).ExecuteAsync();
return _driveService.Files.Delete(entry.Id).ExecuteAsync();
}
public DriveFile InsertEntryIntoFolder(DriveFile entry, string folderId)
@ -393,15 +308,6 @@ internal class GoogleDriveStorage : IDisposable
return request.ExecuteAsync();
}
public DriveFile RemoveEntryFromFolder(DriveFile entry, string folderId)
{
var request = _driveService.Files.Update(FileConstructor(), entry.Id);
request.RemoveParents = folderId;
request.Fields = GoogleLoginProvider.FilesFields;
return request.Execute();
}
public Task<DriveFile> RemoveEntryFromFolderAsync(DriveFile entry, string folderId)
{
var request = _driveService.Files.Update(FileConstructor(), entry.Id);
@ -411,29 +317,9 @@ internal class GoogleDriveStorage : IDisposable
return request.ExecuteAsync();
}
public DriveFile CopyEntry(string toFolderId, string originEntryId)
public async Task<DriveFile> CopyEntryAsync(string toFolderId, string originEntryId, string newTitle)
{
var body = FileConstructor(folderId: toFolderId);
try
{
var request = _driveService.Files.Copy(body, originEntryId);
request.Fields = GoogleLoginProvider.FilesFields;
return request.Execute();
}
catch (GoogleApiException ex)
{
if (ex.HttpStatusCode == HttpStatusCode.Forbidden)
{
throw new SecurityException(ex.Error.Message);
}
throw;
}
}
public async Task<DriveFile> CopyEntryAsync(string toFolderId, string originEntryId)
{
var body = FileConstructor(folderId: toFolderId);
var body = FileConstructor(folderId: toFolderId, title: newTitle);
try
{
var request = _driveService.Files.Copy(body, originEntryId);
@ -451,14 +337,6 @@ internal class GoogleDriveStorage : IDisposable
}
}
public DriveFile RenameEntry(string fileId, string newTitle)
{
var request = _driveService.Files.Update(FileConstructor(newTitle), fileId);
request.Fields = GoogleLoginProvider.FilesFields;
return request.Execute();
}
public Task<DriveFile> RenameEntryAsync(string fileId, string newTitle)
{
var request = _driveService.Files.Update(FileConstructor(newTitle), fileId);
@ -467,27 +345,6 @@ internal class GoogleDriveStorage : IDisposable
return request.ExecuteAsync();
}
public DriveFile SaveStream(string fileId, Stream fileStream, string fileTitle)
{
var mimeType = MimeMapping.GetMimeMapping(fileTitle);
var file = FileConstructor(fileTitle, mimeType);
var request = _driveService.Files.Update(file, fileId, fileStream, mimeType);
request.Fields = GoogleLoginProvider.FilesFields;
var result = request.Upload();
if (result.Exception != null)
{
if (request.ResponseBody == null)
{
throw result.Exception;
}
_logger.ErrorWhileTryingToInsertEntity(result.Exception);
}
return request.ResponseBody;
}
public async Task<DriveFile> SaveStreamAsync(string fileId, Stream fileStream, string fileTitle)
{
var mimeType = MimeMapping.GetMimeMapping(fileTitle);
@ -701,6 +558,83 @@ internal class GoogleDriveStorage : IDisposable
_driveService.Dispose();
}
}
public Task<DriveFile> GetFolderAsync(string folderId)
{
return GetItemAsync(folderId);
}
public Task<DriveFile> GetFileAsync(string fileId)
{
return GetItemAsync(fileId);
}
public Task<DriveFile> CreateFolderAsync(string title, string parentId)
{
return InsertEntryAsync(null, title, parentId, true);
}
public Task<DriveFile> CreateFileAsync(Stream fileStream, string title, string parentId)
{
return InsertEntryAsync(fileStream, title, parentId);
}
public Task<DriveFile> MoveFolderAsync(string folderId, string newFolderName, string toFolderId)
{
var body = FileConstructor(newFolderName, toFolderId);
var request = _driveService.Files.Update(body, folderId);
request.Fields = GoogleLoginProvider.FilesFields;
return request.ExecuteAsync();
}
public Task<DriveFile> MoveFileAsync(string fileId, string newFileName, string toFolderId)
{
var body = FileConstructor(newFileName, toFolderId);
var request = _driveService.Files.Update(body, fileId);
request.Fields = GoogleLoginProvider.FilesFields;
return request.ExecuteAsync();
}
public Task<DriveFile> CopyFolderAsync(string folderId, string newFolderName, string toFolderId)
{
return CopyEntryAsync(toFolderId, folderId, newFolderName);
}
public Task<DriveFile> CopyFileAsync(string fileId, string newFileName, string toFolderId)
{
return CopyEntryAsync(toFolderId, fileId, newFileName);
}
public Task<DriveFile> RenameFolderAsync(string folderId, string newName)
{
return RenameEntryAsync(folderId, newName);
}
public Task<DriveFile> RenameFileAsync(string fileId, string newName)
{
return RenameEntryAsync(fileId, newName);
}
public async Task<DriveFile> SaveStreamAsync(string fileId, Stream fileStream)
{
var file = await GetFileAsync(fileId);
return await SaveStreamAsync(fileId, fileStream, file.Name);
}
public async Task<bool> CheckAccessAsync()
{
try
{
var rootFolder = await GetFolderAsync("root");
return !string.IsNullOrEmpty(rootFolder.Id);
}
catch (UnauthorizedAccessException)
{
return false;
}
}
}
public enum ResumableUploadSessionStatus

View File

@ -0,0 +1,44 @@
// (c) Copyright Ascensio System SIA 2010-2022
//
// This program is a free software product.
// You can redistribute it and/or modify it under the terms
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
// any third-party rights.
//
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
//
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
//
// The interactive user interfaces in modified source and object code versions of the Program must
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
//
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
// trademark law for use of our trademarks.
//
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
namespace ASC.Files.Core.Core.Thirdparty;
internal interface IDaoBase<TFile, TFolder, TItem>
{
string MakeId(object entryId);
string GetParentFolderId(TItem item);
string MakeId(TItem item);
string MakeId(string path = null);
string MakeFolderTitle(TFolder folder);
string MakeFileTitle(TFile file);
Folder<string> ToFolder(TFolder folder);
File<string> ToFile(TFile file);
Task<Folder<string>> GetRootFolderAsync();
Task<TFolder> GetFolderAsync(string folderId);
Task<TFile> GetFileAsync(string fileId);
Task<IEnumerable<string>> GetChildrenAsync(string folderId);
Task<List<TItem>> GetItemsAsync(string parentId, bool? folder = null);
Task<string> GetAvailableTitleAsync(string requestTitle, string parentFolderId, Func<string, string, Task<bool>> isExist);
}

View File

@ -0,0 +1,68 @@
// (c) Copyright Ascensio System SIA 2010-2022
//
// This program is a free software product.
// You can redistribute it and/or modify it under the terms
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
// any third-party rights.
//
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
//
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
//
// The interactive user interfaces in modified source and object code versions of the Program must
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
//
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
// trademark law for use of our trademarks.
//
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
namespace ASC.Files.Core.Core.Thirdparty;
public interface IThirdPartyStorage
{
bool IsOpened { get; }
public void Open(OAuth20Token token);
public void Close();
public Task<long> GetMaxUploadSizeAsync();
public Task<bool> CheckAccessAsync();
public Task<Stream> GetThumbnailAsync(string fileId, int width, int height);
}
public interface IThirdPartyItemStorage<TItem> : IThirdPartyStorage
{
public Task<List<TItem>> GetItemsAsync(string folderId);
public Task DeleteItemAsync(TItem item);
}
public interface IThirdPartyFileStorage<TFile> : IThirdPartyStorage
{
public Task<TFile> GetFileAsync(string fileId);
public Task<TFile> CreateFileAsync(Stream fileStream, string title, string parentId);
public Task<Stream> DownloadStreamAsync(TFile file, int offset = 0);
public Task<TFile> MoveFileAsync(string fileId, string newFileName, string toFolderId);
public Task<TFile> CopyFileAsync(string fileId, string newFileName, string toFolderId);
public Task<TFile> RenameFileAsync(string fileId, string newName);
public Task<TFile> SaveStreamAsync(string fileId, Stream fileStream);
}
public interface IThirdPartyFolderStorage<TFolder> : IThirdPartyStorage
{
public Task<TFolder> GetFolderAsync(string folderId);
public Task<TFolder> CreateFolderAsync(string title, string parentId);
public Task<TFolder> MoveFolderAsync(string folderId, string newFolderName, string toFolderId);
public Task<TFolder> CopyFolderAsync(string folderId, string newFolderName, string toFolderId);
public Task<TFolder> RenameFolderAsync(string folderId, string newName);
}
public interface IThirdPartyStorage<TFile, TFolder, TItem> : IThirdPartyFileStorage<TFile>, IThirdPartyFolderStorage<TFolder>, IThirdPartyItemStorage<TItem>
{
}

View File

@ -26,289 +26,13 @@
namespace ASC.Files.Thirdparty.OneDrive;
[Singletone]
public class OneDriveProviderInfoHelper
{
private readonly ICache _cacheChildItems;
private readonly TimeSpan _cacheExpiration;
private readonly ICache _cacheItem;
private readonly ICacheNotify<OneDriveCacheItem> _cacheNotify;
public OneDriveProviderInfoHelper(ICacheNotify<OneDriveCacheItem> cacheNotify, ICache cache)
{
_cacheExpiration = TimeSpan.FromMinutes(1);
_cacheItem = cache;
_cacheChildItems = cache;
_cacheNotify = cacheNotify;
_cacheNotify.Subscribe((i) =>
{
ResetMemoryCache(i);
if (i.ResetAll)
{
_cacheChildItems.Remove(new Regex($"^{Selectors.OneDrive.Id}i-" + i.Key + ".*"));
_cacheItem.Remove(new Regex($"^{Selectors.OneDrive.Id}-" + i.Key + ".*"));
}
else
{
_cacheChildItems.Remove(new Regex($"{Selectors.OneDrive.Id}i-" + i.Key));
_cacheItem.Remove($"{Selectors.OneDrive.Id}-" + i.Key);
}
}, CacheNotifyAction.Remove);
}
internal async Task CacheResetAsync(int id, string onedriveId = null)
{
var key = id + "-";
var item = new OneDriveCacheItem { Key = key };
if (string.IsNullOrEmpty(onedriveId))
{
item.ResetAll = true;
}
else
{
item.Key += onedriveId;
}
ResetMemoryCache(item);
await _cacheNotify.PublishAsync(item, CacheNotifyAction.Remove);
}
internal async Task<Item> GetOneDriveItemAsync(OneDriveStorage storage, int id, string itemId)
{
var file = _cacheItem.Get<Item>($"{Selectors.OneDrive.Id}-" + id + "-" + itemId);
if (file == null)
{
file = await storage.GetItemAsync(itemId);
if (file != null)
{
_cacheItem.Insert($"{Selectors.OneDrive.Id}-" + id + "-" + itemId, file, DateTime.UtcNow.Add(_cacheExpiration));
}
}
return file;
}
internal async Task<List<Item>> GetOneDriveItemsAsync(OneDriveStorage storage, int id, string onedriveFolderId)
{
var items = _cacheChildItems.Get<List<Item>>($"{Selectors.OneDrive.Id}i-" + id + "-" + onedriveFolderId);
if (items == null)
{
items = await storage.GetItemsAsync(onedriveFolderId);
_cacheChildItems.Insert($"{Selectors.OneDrive.Id}i-" + id + "-" + onedriveFolderId, items, DateTime.UtcNow.Add(_cacheExpiration));
}
return items;
}
internal async Task<Stream> GetThumbnailAsync(OneDriveStorage storage, string onedriveId, int width, int height)
{
return await storage.GetThumbnailAsync(onedriveId, width, height);
}
private void ResetMemoryCache(OneDriveCacheItem i)
{
if (i.ResetAll)
{
_cacheChildItems.Remove(new Regex("^onedrivei-" + i.Key + ".*"));
_cacheItem.Remove(new Regex("^onedrive-" + i.Key + ".*"));
}
else
{
_cacheChildItems.Remove(new Regex($"{Selectors.OneDrive.Id}i-" + i.Key));
_cacheItem.Remove($"{Selectors.OneDrive.Id}-" + i.Key);
}
}
}
[Transient]
[DebuggerDisplay("{CustomerTitle}")]
internal class OneDriveProviderInfo : IProviderInfo
internal class OneDriveProviderInfo : AbstractProviderInfo<Item, Item, Item, OneDriveLoginProvider>
{
private readonly OneDriveProviderInfoHelper _oneDriveProviderInfoHelper;
private readonly OneDriveStorageDisposableWrapper _wrapper;
internal override string Selector { get; } = Selectors.OneDrive.Id;
public OneDriveProviderInfo(
OneDriveStorageDisposableWrapper wrapper,
OneDriveProviderInfoHelper oneDriveProviderInfoHelper)
public OneDriveProviderInfo(DisposableWrapper wrapper) : base(wrapper)
{
_wrapper = wrapper;
_oneDriveProviderInfoHelper = oneDriveProviderInfoHelper;
}
public DateTime CreateOn { get; set; }
public string CustomerTitle { get; set; }
public string FolderId { get; set; }
public FolderType FolderType { get; set; }
public bool HasLogo { get; set; }
public int ID { get; set; }
public Guid Owner { get; set; }
public bool Private { get; set; }
public string ProviderKey { get; set; }
public string RootFolderId => $"{Selectors.OneDrive.Id}-" + ID;
public FolderType RootFolderType { get; set; }
public OAuth20Token Token { get; set; }
internal bool StorageOpened => _wrapper.TryGetStorage(ID, out var storage) && storage.IsOpened;
internal Task<OneDriveStorage> StorageAsync
{
get
{
if (!_wrapper.TryGetStorage(ID, out var storage) || !storage.IsOpened)
{
return _wrapper.CreateStorageAsync(Token, ID);
}
return Task.FromResult(storage);
}
}
public async Task<bool> CheckAccessAsync()
{
try
{
var storage = await StorageAsync;
return await storage.CheckAccessAsync();
}
catch (AggregateException)
{
return false;
}
}
public void Dispose()
{
if (StorageOpened)
{
StorageAsync.Result.Close();
}
}
public Task InvalidateStorageAsync()
{
if (_wrapper != null)
{
_wrapper.Dispose();
}
return CacheResetAsync();
}
public void UpdateTitle(string newtitle)
{
CustomerTitle = newtitle;
}
internal Task CacheResetAsync(string onedriveId = null)
{
return _oneDriveProviderInfoHelper.CacheResetAsync(ID, onedriveId);
}
internal async Task<Item> GetOneDriveItemAsync(string itemId)
{
var storage = await StorageAsync;
return await _oneDriveProviderInfoHelper.GetOneDriveItemAsync(storage, ID, itemId);
}
internal async Task<List<Item>> GetOneDriveItemsAsync(string onedriveFolderId)
{
var storage = await StorageAsync;
return await _oneDriveProviderInfoHelper.GetOneDriveItemsAsync(storage, ID, onedriveFolderId);
}
internal async Task<Stream> GetThumbnailAsync(string onedriveId, int width, int height)
{
var storage = await StorageAsync;
return await _oneDriveProviderInfoHelper.GetThumbnailAsync(storage, onedriveId, width, height);
}
}
[Transient(Additional = typeof(OneDriveProviderInfoExtention))]
internal class OneDriveStorageDisposableWrapper : IDisposable
{
internal readonly ConsumerFactory ConsumerFactory;
internal readonly IServiceProvider ServiceProvider;
private readonly OAuth20TokenHelper _oAuth20TokenHelper;
private readonly ConcurrentDictionary<int, OneDriveStorage> _storages =
new ConcurrentDictionary<int, OneDriveStorage>();
public OneDriveStorageDisposableWrapper(ConsumerFactory consumerFactory, IServiceProvider serviceProvider, OAuth20TokenHelper oAuth20TokenHelper)
{
ConsumerFactory = consumerFactory;
ServiceProvider = serviceProvider;
_oAuth20TokenHelper = oAuth20TokenHelper;
}
public Task<OneDriveStorage> CreateStorageAsync(OAuth20Token token, int id)
{
if (TryGetStorage(id, out var storage) && storage.IsOpened)
{
return Task.FromResult(storage);
}
return InternalCreateStorageAsync(token, id);
}
public bool TryGetStorage(int id, out OneDriveStorage storage)
{
return _storages.TryGetValue(id, out storage);
}
public void Dispose()
{
foreach (var (key, storage) in _storages)
{
if (storage.IsOpened)
{
storage.Close();
_storages.Remove(key, out _);
}
}
}
private Task CheckTokenAsync(OAuth20Token token, int id)
{
if (token == null)
{
throw new UnauthorizedAccessException("Cannot create GoogleDrive session with given token");
}
return InternalCheckTokenAsync(token, id);
}
private async Task InternalCheckTokenAsync(OAuth20Token token, int id)
{
if (token.IsExpired)
{
token = _oAuth20TokenHelper.RefreshToken<OneDriveLoginProvider>(ConsumerFactory, token);
var dbDao = ServiceProvider.GetService<ProviderAccountDao>();
var authData = new AuthData(token: token.ToJson());
await dbDao.UpdateProviderInfoAsync(id, authData);
}
}
private async Task<OneDriveStorage> InternalCreateStorageAsync(OAuth20Token token, int id)
{
var oneDriveStorage = ServiceProvider.GetRequiredService<OneDriveStorage>();
await CheckTokenAsync(token, id);
oneDriveStorage.Open(token);
_storages.TryAdd(id, oneDriveStorage);
return oneDriveStorage;
}
}
public static class OneDriveProviderInfoExtention
{
public static void Register(DIHelper dIHelper)
{
dIHelper.TryAdd<OneDriveStorage>();
}
}

View File

@ -29,7 +29,7 @@ using Folder = Microsoft.OneDrive.Sdk.Folder;
namespace ASC.Files.Thirdparty.OneDrive;
[Transient]
internal class OneDriveStorage
internal class OneDriveStorage : IThirdPartyStorage<Item, Item, Item>
{
private OAuth20Token _token;
@ -128,7 +128,7 @@ internal class OneDriveStorage
}
}
public async Task<List<Item>> GetItemsAsync(string folderId, int limit = 500)
public async Task<List<Item>> GetItemsAsync(string folderId)
{
return new List<Item>(await GetItemRequest(folderId).Children.Request().GetAsync());
}
@ -378,6 +378,51 @@ internal class OneDriveStorage
return null;
}
}
public Task<Item> GetFolderAsync(string folderId)
{
return GetItemAsync(folderId);
}
public Task<Item> GetFileAsync(string fileId)
{
return GetItemAsync(fileId);
}
public Task<Item> MoveFolderAsync(string folderId, string newFolderName, string toFolderId)
{
return MoveItemAsync(folderId, newFolderName, toFolderId);
}
public Task<Item> MoveFileAsync(string fileId, string newFileName, string toFolderId)
{
return MoveItemAsync(fileId, newFileName, toFolderId);
}
public Task<Item> CopyFolderAsync(string folderId, string newFolderName, string toFolderId)
{
return CopyItemAsync(folderId, newFolderName, toFolderId);
}
public Task<Item> CopyFileAsync(string fileId, string newFileName, string toFolderId)
{
return CopyItemAsync(fileId, newFileName, toFolderId);
}
public Task<Item> RenameFolderAsync(string folderId, string newName)
{
return RenameItemAsync(folderId, newName);
}
public Task<Item> RenameFileAsync(string fileId, string newName)
{
return RenameItemAsync(fileId, newName);
}
public Task<long> GetMaxUploadSizeAsync()
{
return Task.FromResult(MaxChunkedUploadFileSize);
}
}

View File

@ -34,6 +34,7 @@ public static class Selectors
public static readonly Selector OneDrive = new Selector() { Name = "OneDrive", Id = "onedrive" };
public static readonly List<Selector> All = new List<Selector>() { SharpBox, SharePoint, GoogleDrive, Box, Dropbox, OneDrive };
public static readonly List<Selector> StoredCache = new List<Selector>() { GoogleDrive, Box, Dropbox, OneDrive };
}
public class Selector

View File

@ -523,7 +523,7 @@ internal abstract class ThirdPartyProviderDao<T> : ThirdPartyProviderDao, IDispo
{
fileEntry.CreateBy = ProviderInfo.Owner;
fileEntry.ModifiedBy = ProviderInfo.Owner;
fileEntry.ProviderId = ProviderInfo.ID;
fileEntry.ProviderId = ProviderInfo.ProviderId;
fileEntry.ProviderKey = ProviderInfo.ProviderKey;
fileEntry.RootCreateBy = ProviderInfo.Owner;
fileEntry.RootFolderType = ProviderInfo.RootFolderType;
@ -566,7 +566,7 @@ internal abstract class ThirdPartyProviderDao<T> : ThirdPartyProviderDao, IDispo
FilterType.MediaOnly;
}
protected abstract string MakeId(string path = null);
public abstract string MakeId(string path = null);
public async IAsyncEnumerable<Tag> GetNewTagsAsync(Guid subject, Folder<string> parentFolder, bool deepSearch)
{
@ -633,7 +633,7 @@ internal abstract class ThirdPartyProviderDao<T> : ThirdPartyProviderDao, IDispo
}
}
protected abstract Task<IEnumerable<string>> GetChildrenAsync(string folderId);
public abstract Task<IEnumerable<string>> GetChildrenAsync(string folderId);
public void Dispose()
{