From 40b811733282705caf5e6dcaf80c5d5c85c727eb Mon Sep 17 00:00:00 2001 From: Mauro van der Gun Date: Tue, 2 Jan 2024 09:46:32 -0400 Subject: [PATCH 1/8] add ftp adapter --- .../src/AmazonS3Adapter.cs | 3 +- .../src/AzureBlobStorageAdapter.cs | 3 +- .../src/AzureFileStorageAdapter.cs | 3 +- .../src/DropboxAdapter.cs | 3 +- .../FileSystem.Adapters.Ftp.csproj | 31 +++ FileSystem.Adapters.Ftp/src/FtpAdapter.cs | 253 ++++++++++++++++++ .../src/FtpAdapterConfiguration.cs | 8 + FileSystem.Adapters.Ftp/src/ModelFactory.cs | 33 +++ .../src/GoogleDriveAdapter.cs | 3 +- .../src/MicrosoftOneDriveAdapter.cs | 3 +- .../FileSystem.Adapters.Sftp.csproj | 2 +- FileSystem.Adapters.Sftp/src/SftpAdapter.cs | 6 +- FileSystem.sln | 6 + FileSystem/FileSystem.csproj | 2 +- FileSystem/src/Adapters/Adapter.cs | 7 +- FileSystem/src/Adapters/IAdapter.cs | 2 +- FileSystem/src/Adapters/LocalAdapter.cs | 7 +- FileSystem/src/Constants/AdapterConstants.cs | 7 - .../src/Constants/FileSystemConstants.cs | 10 + .../Exceptions/InvalidVirtualPathException.cs | 17 ++ FileSystem/src/FileSystem.cs | 82 ++++-- FileSystem/src/Utilities/PathUtilities.cs | 22 ++ FileSystem/src/Utilities/StreamUtilities.cs | 2 +- README.md | 16 ++ .../AmazonS3AdapterTest.cs | 6 +- .../AzureBlobStorageAdapterTest.cs | 6 +- .../AzureFileStorageAdapterTest.cs | 6 +- .../GoogleDriveAdapterTest.cs | 6 +- .../MicrosoftOneDriveAdapterTest.cs | 6 +- .../SftpAdapterTest.cs | 6 +- 30 files changed, 493 insertions(+), 74 deletions(-) create mode 100644 FileSystem.Adapters.Ftp/FileSystem.Adapters.Ftp.csproj create mode 100644 FileSystem.Adapters.Ftp/src/FtpAdapter.cs create mode 100644 FileSystem.Adapters.Ftp/src/FtpAdapterConfiguration.cs create mode 100644 FileSystem.Adapters.Ftp/src/ModelFactory.cs delete mode 100644 FileSystem/src/Constants/AdapterConstants.cs create mode 100644 FileSystem/src/Constants/FileSystemConstants.cs create mode 100644 FileSystem/src/Exceptions/InvalidVirtualPathException.cs diff --git a/FileSystem.Adapters.AmazonS3/src/AmazonS3Adapter.cs b/FileSystem.Adapters.AmazonS3/src/AmazonS3Adapter.cs index 8c18873..4e95be8 100644 --- a/FileSystem.Adapters.AmazonS3/src/AmazonS3Adapter.cs +++ b/FileSystem.Adapters.AmazonS3/src/AmazonS3Adapter.cs @@ -31,9 +31,10 @@ public override void Dispose() client.Dispose(); } - public override void Connect() + public override async Task ConnectAsync(CancellationToken cancellationToken = default) { Logger.LogStartConnectingAdapter(this); + await Task.CompletedTask; Logger.LogFinishedConnectingAdapter(this); } diff --git a/FileSystem.Adapters.AzureBlobStorage/src/AzureBlobStorageAdapter.cs b/FileSystem.Adapters.AzureBlobStorage/src/AzureBlobStorageAdapter.cs index 43d0dfe..6333eba 100644 --- a/FileSystem.Adapters.AzureBlobStorage/src/AzureBlobStorageAdapter.cs +++ b/FileSystem.Adapters.AzureBlobStorage/src/AzureBlobStorageAdapter.cs @@ -28,9 +28,10 @@ public override void Dispose() { } - public override void Connect() + public override async Task ConnectAsync(CancellationToken cancellationToken = default) { Logger.LogStartConnectingAdapter(this); + await Task.CompletedTask; Logger.LogFinishedConnectingAdapter(this); } diff --git a/FileSystem.Adapters.AzureFileStorage/src/AzureFileStorageAdapter.cs b/FileSystem.Adapters.AzureFileStorage/src/AzureFileStorageAdapter.cs index 24a9876..19378dd 100644 --- a/FileSystem.Adapters.AzureFileStorage/src/AzureFileStorageAdapter.cs +++ b/FileSystem.Adapters.AzureFileStorage/src/AzureFileStorageAdapter.cs @@ -27,9 +27,10 @@ public override void Dispose() { } - public override void Connect() + public override async Task ConnectAsync(CancellationToken cancellationToken = default) { Logger.LogStartConnectingAdapter(this); + await Task.CompletedTask; Logger.LogFinishedConnectingAdapter(this); } diff --git a/FileSystem.Adapters.Dropbox/src/DropboxAdapter.cs b/FileSystem.Adapters.Dropbox/src/DropboxAdapter.cs index 6ff9106..7f40622 100644 --- a/FileSystem.Adapters.Dropbox/src/DropboxAdapter.cs +++ b/FileSystem.Adapters.Dropbox/src/DropboxAdapter.cs @@ -29,9 +29,10 @@ public override void Dispose() client.Dispose(); } - public override void Connect() + public override async Task ConnectAsync(CancellationToken cancellationToken = default) { Logger.LogStartConnectingAdapter(this); + await Task.CompletedTask; Logger.LogFinishedConnectingAdapter(this); } diff --git a/FileSystem.Adapters.Ftp/FileSystem.Adapters.Ftp.csproj b/FileSystem.Adapters.Ftp/FileSystem.Adapters.Ftp.csproj new file mode 100644 index 0000000..6c8c872 --- /dev/null +++ b/FileSystem.Adapters.Ftp/FileSystem.Adapters.Ftp.csproj @@ -0,0 +1,31 @@ + + + + SharpGrip.FileSystem.Adapters.Ftp + + + + SharpGrip.FileSystem.Adapters.Ftp + SharpGrip.FileSystem.Adapters.Ftp + SharpGrip FileSystem FTP adapter + The SharpGrip FileSystem FTP adapter. + sharpgrip;file-system;ftp + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + diff --git a/FileSystem.Adapters.Ftp/src/FtpAdapter.cs b/FileSystem.Adapters.Ftp/src/FtpAdapter.cs new file mode 100644 index 0000000..d95e289 --- /dev/null +++ b/FileSystem.Adapters.Ftp/src/FtpAdapter.cs @@ -0,0 +1,253 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; +using FluentFTP; +using FluentFTP.Exceptions; +using SharpGrip.FileSystem.Constants; +using SharpGrip.FileSystem.Exceptions; +using SharpGrip.FileSystem.Extensions; +using SharpGrip.FileSystem.Models; +using SharpGrip.FileSystem.Utilities; +using DirectoryNotFoundException = SharpGrip.FileSystem.Exceptions.DirectoryNotFoundException; +using FileNotFoundException = SharpGrip.FileSystem.Exceptions.FileNotFoundException; + +namespace SharpGrip.FileSystem.Adapters.Ftp +{ + public class FtpAdapter : Adapter + { + private readonly IAsyncFtpClient client; + + public FtpAdapter(string prefix, string rootPath, IAsyncFtpClient client, Action? configuration = null) : base(prefix, rootPath, configuration) + { + this.client = client; + } + + public override void Dispose() + { + client.Dispose(); + } + + public override async Task ConnectAsync(CancellationToken cancellationToken = default) + { + if (client.IsConnected) + { + return; + } + + try + { + Logger.LogStartConnectingAdapter(this); + await client.Connect(cancellationToken); + Logger.LogFinishedConnectingAdapter(this); + } + catch (Exception exception) + { + throw Exception(exception); + } + } + + public override async Task GetFileAsync(string virtualPath, CancellationToken cancellationToken = default) + { + var path = GetPath(virtualPath); + + try + { + var file = await client.GetObjectInfo(path, token: cancellationToken); + + if (file == null || file.Type != FtpObjectType.File) + { + throw new FileNotFoundException(path, Prefix); + } + + return ModelFactory.CreateFile(file, virtualPath); + } + catch (Exception exception) + { + throw Exception(exception); + } + } + + public override async Task GetDirectoryAsync(string virtualPath, CancellationToken cancellationToken = default) + { + var path = GetPath(virtualPath); + + try + { + var directory = await client.GetObjectInfo(path, token: cancellationToken); + + if (directory == null || directory.Type != FtpObjectType.Directory) + { + throw new DirectoryNotFoundException(path, Prefix); + } + + return ModelFactory.CreateDirectory(directory, virtualPath); + } + catch (Exception exception) + { + throw Exception(exception); + } + } + + public override async Task> GetFilesAsync(string virtualPath = "", CancellationToken cancellationToken = default) + { + await GetDirectoryAsync(virtualPath, cancellationToken); + var path = GetPath(virtualPath); + + try + { + var ftpListItems = await client.GetListing(path, cancellationToken); + + return ftpListItems.Where(file => file.Type == FtpObjectType.File).Select(file => ModelFactory.CreateFile(file, GetVirtualPath(file.FullName))); + } + catch (Exception exception) + { + throw Exception(exception); + } + } + + public override async Task> GetDirectoriesAsync(string virtualPath = "", CancellationToken cancellationToken = default) + { + await GetDirectoryAsync(virtualPath, cancellationToken); + var path = GetPath(virtualPath); + + try + { + var ftpListItems = await client.GetListing(path, cancellationToken); + + return ftpListItems.Where(file => file.Type == FtpObjectType.Directory).Select(file => ModelFactory.CreateDirectory(file, GetVirtualPath(file.FullName))); + } + catch (Exception exception) + { + throw Exception(exception); + } + } + + public override async Task CreateDirectoryAsync(string virtualPath, CancellationToken cancellationToken = default) + { + if (await DirectoryExistsAsync(virtualPath, cancellationToken)) + { + throw new DirectoryExistsException(GetPath(virtualPath), Prefix); + } + + try + { + await client.CreateDirectory(GetPath(virtualPath), cancellationToken); + } + catch (Exception exception) + { + throw Exception(exception); + } + } + + public override async Task DeleteDirectoryAsync(string virtualPath, CancellationToken cancellationToken = default) + { + await GetDirectoryAsync(virtualPath, cancellationToken); + + try + { + await client.DeleteDirectory(GetPath(virtualPath), cancellationToken); + } + catch (Exception exception) + { + throw Exception(exception); + } + } + + public override async Task DeleteFileAsync(string virtualPath, CancellationToken cancellationToken = default) + { + await GetFileAsync(virtualPath, cancellationToken); + + try + { + await client.DeleteFile(GetPath(virtualPath), cancellationToken); + } + catch (Exception exception) + { + throw Exception(exception); + } + } + + public override async Task ReadFileStreamAsync(string virtualPath, CancellationToken cancellationToken = default) + { + await GetFileAsync(virtualPath, cancellationToken); + + try + { + var fileStream = await client.OpenRead(GetPath(virtualPath), token: cancellationToken); + + return await StreamUtilities.CopyContentsToMemoryStreamAsync(fileStream, true, cancellationToken); + } + catch (Exception exception) + { + throw Exception(exception); + } + } + + public override async Task WriteFileAsync(string virtualPath, Stream contents, bool overwrite = false, CancellationToken cancellationToken = default) + { + if (!overwrite && await FileExistsAsync(virtualPath, cancellationToken)) + { + throw new FileExistsException(GetPath(virtualPath), Prefix); + } + + try + { + contents.Seek(0, SeekOrigin.Begin); + + using var writeStream = await client.OpenWrite(GetPath(virtualPath), token: cancellationToken); + + await contents.CopyToAsync(writeStream, FileSystemConstants.Streaming.DefaultMemoryStreamBufferSize, cancellationToken); + await writeStream.FlushAsync(cancellationToken); + } + catch (Exception exception) + { + throw Exception(exception); + } + } + + public override async Task AppendFileAsync(string virtualPath, Stream contents, CancellationToken cancellationToken = default) + { + await GetFileAsync(virtualPath, cancellationToken); + + try + { + using var fileStream = await client.OpenAppend(GetPath(virtualPath), token: cancellationToken); + + await contents.CopyToAsync(fileStream); + } + catch (Exception exception) + { + throw new AdapterRuntimeException(exception); + } + } + + protected override Exception Exception(Exception exception) + { + if (exception is FileSystemException) + { + return exception; + } + + if (exception is SocketException socketException) + { + return new ConnectionException(socketException); + } + + if (exception is FtpAuthenticationException ftpAuthenticationException) + { + return new ConnectionException(ftpAuthenticationException); + } + + if (exception is FtpSecurityNotAvailableException ftpSecurityNotAvailableException) + { + return new ConnectionException(ftpSecurityNotAvailableException); + } + + return new AdapterRuntimeException(exception); + } + } +} \ No newline at end of file diff --git a/FileSystem.Adapters.Ftp/src/FtpAdapterConfiguration.cs b/FileSystem.Adapters.Ftp/src/FtpAdapterConfiguration.cs new file mode 100644 index 0000000..6d5764c --- /dev/null +++ b/FileSystem.Adapters.Ftp/src/FtpAdapterConfiguration.cs @@ -0,0 +1,8 @@ +using SharpGrip.FileSystem.Configuration; + +namespace SharpGrip.FileSystem.Adapters.Ftp +{ + public class FtpAdapterConfiguration : AdapterConfiguration + { + } +} \ No newline at end of file diff --git a/FileSystem.Adapters.Ftp/src/ModelFactory.cs b/FileSystem.Adapters.Ftp/src/ModelFactory.cs new file mode 100644 index 0000000..7bced46 --- /dev/null +++ b/FileSystem.Adapters.Ftp/src/ModelFactory.cs @@ -0,0 +1,33 @@ +using FluentFTP; +using SharpGrip.FileSystem.Models; + +namespace SharpGrip.FileSystem.Adapters.Ftp +{ + public static class ModelFactory + { + public static IFile CreateFile(FtpListItem file, string virtualPath) + { + return new FileModel + { + Name = file.Name, + Path = file.FullName, + VirtualPath = virtualPath, + Length = file.Size, + LastModifiedDateTime = file.Modified, + CreatedDateTime = file.Created + }; + } + + public static DirectoryModel CreateDirectory(FtpListItem directory, string virtualPath) + { + return new DirectoryModel + { + Name = directory.Name, + Path = directory.FullName, + VirtualPath = virtualPath, + LastModifiedDateTime = directory.Modified, + CreatedDateTime = directory.Created + }; + } + } +} \ No newline at end of file diff --git a/FileSystem.Adapters.GoogleDrive/src/GoogleDriveAdapter.cs b/FileSystem.Adapters.GoogleDrive/src/GoogleDriveAdapter.cs index c0f7f28..0b26edc 100644 --- a/FileSystem.Adapters.GoogleDrive/src/GoogleDriveAdapter.cs +++ b/FileSystem.Adapters.GoogleDrive/src/GoogleDriveAdapter.cs @@ -40,9 +40,10 @@ public override void Dispose() client.Dispose(); } - public override void Connect() + public override async Task ConnectAsync(CancellationToken cancellationToken = default) { Logger.LogStartConnectingAdapter(this); + await Task.CompletedTask; Logger.LogFinishedConnectingAdapter(this); } diff --git a/FileSystem.Adapters.MicrosoftOneDrive/src/MicrosoftOneDriveAdapter.cs b/FileSystem.Adapters.MicrosoftOneDrive/src/MicrosoftOneDriveAdapter.cs index 60d0ff5..15adbed 100644 --- a/FileSystem.Adapters.MicrosoftOneDrive/src/MicrosoftOneDriveAdapter.cs +++ b/FileSystem.Adapters.MicrosoftOneDrive/src/MicrosoftOneDriveAdapter.cs @@ -29,9 +29,10 @@ public override void Dispose() { } - public override void Connect() + public override async Task ConnectAsync(CancellationToken cancellationToken = default) { Logger.LogStartConnectingAdapter(this); + await Task.CompletedTask; Logger.LogFinishedConnectingAdapter(this); } diff --git a/FileSystem.Adapters.Sftp/FileSystem.Adapters.Sftp.csproj b/FileSystem.Adapters.Sftp/FileSystem.Adapters.Sftp.csproj index a8c6114..6fd1441 100644 --- a/FileSystem.Adapters.Sftp/FileSystem.Adapters.Sftp.csproj +++ b/FileSystem.Adapters.Sftp/FileSystem.Adapters.Sftp.csproj @@ -21,7 +21,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/FileSystem.Adapters.Sftp/src/SftpAdapter.cs b/FileSystem.Adapters.Sftp/src/SftpAdapter.cs index 8c0aad4..dc9e6ce 100644 --- a/FileSystem.Adapters.Sftp/src/SftpAdapter.cs +++ b/FileSystem.Adapters.Sftp/src/SftpAdapter.cs @@ -35,7 +35,7 @@ public override void Dispose() ((IBaseClient) client).Dispose(); } - public override void Connect() + public override async Task ConnectAsync(CancellationToken cancellationToken = default) { if (client.IsConnected) { @@ -45,7 +45,7 @@ public override void Connect() try { Logger.LogStartConnectingAdapter(this); - client.Connect(); + await client.ConnectAsync(cancellationToken); Logger.LogFinishedConnectingAdapter(this); } catch (Exception exception) @@ -224,7 +224,7 @@ public override async Task WriteFileAsync(string virtualPath, Stream contents, b using var writeStream = client.OpenWrite(GetPath(virtualPath)); - await contents.CopyToAsync(writeStream, AdapterConstants.DefaultMemoryStreamBufferSize, cancellationToken); + await contents.CopyToAsync(writeStream, FileSystemConstants.Streaming.DefaultMemoryStreamBufferSize, cancellationToken); await writeStream.FlushAsync(cancellationToken); } catch (Exception exception) diff --git a/FileSystem.sln b/FileSystem.sln index 39ed7fe..46362fa 100644 --- a/FileSystem.sln +++ b/FileSystem.sln @@ -10,6 +10,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileSystem.Adapters.AzureFi EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileSystem.Adapters.Dropbox", "FileSystem.Adapters.Dropbox\FileSystem.Adapters.Dropbox.csproj", "{FA70DD02-3D25-44C6-ACC8-D61BA55F6C6F}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileSystem.Adapters.Ftp", "FileSystem.Adapters.Ftp\FileSystem.Adapters.Ftp.csproj", "{ED38402A-3667-4701-A974-4D7710E1544A}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileSystem.Adapters.GoogleDrive", "FileSystem.Adapters.GoogleDrive\FileSystem.Adapters.GoogleDrive.csproj", "{CB1F411C-3FB5-4441-9541-423951C17BAF}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileSystem.Adapters.MicrosoftOneDrive", "FileSystem.Adapters.MicrosoftOneDrive\FileSystem.Adapters.MicrosoftOneDrive.csproj", "{9955422E-4A50-43CD-BC2B-91E6820F206C}" @@ -44,6 +46,10 @@ Global {FA70DD02-3D25-44C6-ACC8-D61BA55F6C6F}.Debug|Any CPU.Build.0 = Debug|Any CPU {FA70DD02-3D25-44C6-ACC8-D61BA55F6C6F}.Release|Any CPU.ActiveCfg = Release|Any CPU {FA70DD02-3D25-44C6-ACC8-D61BA55F6C6F}.Release|Any CPU.Build.0 = Release|Any CPU + {ED38402A-3667-4701-A974-4D7710E1544A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ED38402A-3667-4701-A974-4D7710E1544A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ED38402A-3667-4701-A974-4D7710E1544A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ED38402A-3667-4701-A974-4D7710E1544A}.Release|Any CPU.Build.0 = Release|Any CPU {CB1F411C-3FB5-4441-9541-423951C17BAF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CB1F411C-3FB5-4441-9541-423951C17BAF}.Debug|Any CPU.Build.0 = Debug|Any CPU {CB1F411C-3FB5-4441-9541-423951C17BAF}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/FileSystem/FileSystem.csproj b/FileSystem/FileSystem.csproj index 7c7fffc..2285baa 100644 --- a/FileSystem/FileSystem.csproj +++ b/FileSystem/FileSystem.csproj @@ -9,7 +9,7 @@ SharpGrip.FileSystem SharpGrip FileSystem SharpGrip FileSystem is a file system abstraction supporting multiple adapters. - sharpgrip;file-system;amazon-s3;azure-blob-storage;azure-file-storage;dropbox;microsoft-onedrive;sftp + sharpgrip;file-system;amazon-s3;azure-blob-storage;azure-file-storage;dropbox;ftp;google-drive;microsoft-onedrive;sftp diff --git a/FileSystem/src/Adapters/Adapter.cs b/FileSystem/src/Adapters/Adapter.cs index aa39db7..a977c21 100644 --- a/FileSystem/src/Adapters/Adapter.cs +++ b/FileSystem/src/Adapters/Adapter.cs @@ -20,7 +20,7 @@ namespace SharpGrip.FileSystem.Adapters { public string Prefix { get; } public string RootPath { get; } - public string Name => GetType().FullName!; + public string Name { get; } public IAdapterConfiguration AdapterConfiguration => Configuration; protected TAdapterConfiguration Configuration { get; } protected ILogger Logger { get; } = NullLogger>.Instance; @@ -32,6 +32,7 @@ protected Adapter(string prefix, string rootPath, Action? var adapterConfiguration = new TAdapterConfiguration(); configuration?.Invoke(adapterConfiguration); + Name = GetType().FullName!; Configuration = adapterConfiguration; if (Configuration.EnableLogging) @@ -96,7 +97,7 @@ public virtual async Task AppendFileAsync(string virtualPath, Stream contents, C using var memoryStream = await StreamUtilities.CopyContentsToMemoryStreamAsync(fileContents, false, cancellationToken); - await contents.CopyToAsync(memoryStream, AdapterConstants.DefaultMemoryStreamBufferSize, cancellationToken); + await contents.CopyToAsync(memoryStream, FileSystemConstants.Streaming.DefaultMemoryStreamBufferSize, cancellationToken); memoryStream.Seek(0, SeekOrigin.Begin); await DeleteFileAsync(virtualPath, cancellationToken); @@ -116,7 +117,7 @@ public virtual async Task AppendFileAsync(string virtualPath, Stream contents, C } public abstract void Dispose(); - public abstract void Connect(); + public abstract Task ConnectAsync(CancellationToken cancellationToken = default); public abstract Task GetFileAsync(string virtualPath, CancellationToken cancellationToken = default); public abstract Task GetDirectoryAsync(string virtualPath, CancellationToken cancellationToken = default); public abstract Task> GetFilesAsync(string virtualPath = "", CancellationToken cancellationToken = default); diff --git a/FileSystem/src/Adapters/IAdapter.cs b/FileSystem/src/Adapters/IAdapter.cs index 3839204..496959f 100644 --- a/FileSystem/src/Adapters/IAdapter.cs +++ b/FileSystem/src/Adapters/IAdapter.cs @@ -14,7 +14,7 @@ public interface IAdapter : IDisposable public string RootPath { get; } public string Name { get; } public IAdapterConfiguration AdapterConfiguration { get; } - public void Connect(); + public Task ConnectAsync(CancellationToken cancellationToken = default); public void ClearCache(); public Task GetFileAsync(string virtualPath, CancellationToken cancellationToken = default); public Task GetDirectoryAsync(string virtualPath, CancellationToken cancellationToken = default); diff --git a/FileSystem/src/Adapters/LocalAdapter.cs b/FileSystem/src/Adapters/LocalAdapter.cs index 1837a92..3a52558 100644 --- a/FileSystem/src/Adapters/LocalAdapter.cs +++ b/FileSystem/src/Adapters/LocalAdapter.cs @@ -22,9 +22,10 @@ public override void Dispose() { } - public override void Connect() + public override async Task ConnectAsync(CancellationToken cancellationToken = default) { Logger.LogStartConnectingAdapter(this); + await Task.CompletedTask; Logger.LogFinishedConnectingAdapter(this); } @@ -196,7 +197,7 @@ public override async Task WriteFileAsync(string virtualPath, Stream contents, b using var fileStream = new FileStream(GetPath(virtualPath), FileMode.Create); contents.Seek(0, SeekOrigin.Begin); - await contents.CopyToAsync(fileStream, AdapterConstants.DefaultMemoryStreamBufferSize, cancellationToken); + await contents.CopyToAsync(fileStream, FileSystemConstants.Streaming.DefaultMemoryStreamBufferSize, cancellationToken); } catch (Exception exception) { @@ -213,7 +214,7 @@ public override async Task AppendFileAsync(string virtualPath, Stream contents, using var fileStream = new FileStream(GetPath(virtualPath), FileMode.Append); contents.Seek(0, SeekOrigin.Begin); - await contents.CopyToAsync(fileStream, AdapterConstants.DefaultMemoryStreamBufferSize, cancellationToken); + await contents.CopyToAsync(fileStream, FileSystemConstants.Streaming.DefaultMemoryStreamBufferSize, cancellationToken); } catch (Exception exception) { diff --git a/FileSystem/src/Constants/AdapterConstants.cs b/FileSystem/src/Constants/AdapterConstants.cs deleted file mode 100644 index 6b18777..0000000 --- a/FileSystem/src/Constants/AdapterConstants.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace SharpGrip.FileSystem.Constants -{ - public static class AdapterConstants - { - public const int DefaultMemoryStreamBufferSize = 81920; - } -} \ No newline at end of file diff --git a/FileSystem/src/Constants/FileSystemConstants.cs b/FileSystem/src/Constants/FileSystemConstants.cs new file mode 100644 index 0000000..2c14b4c --- /dev/null +++ b/FileSystem/src/Constants/FileSystemConstants.cs @@ -0,0 +1,10 @@ +namespace SharpGrip.FileSystem.Constants +{ + public static class FileSystemConstants + { + public static class Streaming + { + public const int DefaultMemoryStreamBufferSize = 81920; + } + } +} \ No newline at end of file diff --git a/FileSystem/src/Exceptions/InvalidVirtualPathException.cs b/FileSystem/src/Exceptions/InvalidVirtualPathException.cs new file mode 100644 index 0000000..71d26b5 --- /dev/null +++ b/FileSystem/src/Exceptions/InvalidVirtualPathException.cs @@ -0,0 +1,17 @@ +namespace SharpGrip.FileSystem.Exceptions +{ + public class InvalidVirtualPathException : FileSystemException + { + public string VirtualPath { get; } + + public InvalidVirtualPathException(string virtualPath) : base(GetMessage(virtualPath)) + { + VirtualPath = virtualPath; + } + + private static string GetMessage(string virtualPath) + { + return $"Invalid virtual path '{virtualPath}'."; + } + } +} \ No newline at end of file diff --git a/FileSystem/src/FileSystem.cs b/FileSystem/src/FileSystem.cs index 983b7c2..28d62ce 100644 --- a/FileSystem/src/FileSystem.cs +++ b/FileSystem/src/FileSystem.cs @@ -154,23 +154,25 @@ public IFile GetFile(string virtualPath) /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. /// Thrown if the file does not exists at the given path. - public Task GetFileAsync(string virtualPath, CancellationToken cancellationToken = default) + public async Task GetFileAsync(string virtualPath, CancellationToken cancellationToken = default) { try { Logger.LogStartExecutingMethod(nameof(GetFileAsync)); + virtualPath = PathUtilities.NormalizeVirtualPath(virtualPath); + var prefix = PathUtilities.GetPrefix(virtualPath); var adapter = GetAdapter(prefix); - adapter.Connect(); + await adapter.ConnectAsync(cancellationToken); if (!adapter.AdapterConfiguration.EnableCache) { adapter.ClearCache(); } - return adapter.GetFileAsync(virtualPath, cancellationToken); + return await adapter.GetFileAsync(virtualPath, cancellationToken); } catch (FileSystemException fileSystemException) { @@ -213,23 +215,25 @@ public IDirectory GetDirectory(string virtualPath) /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. /// Thrown if the directory does not exists at the given path. - public Task GetDirectoryAsync(string virtualPath, CancellationToken cancellationToken = default) + public async Task GetDirectoryAsync(string virtualPath, CancellationToken cancellationToken = default) { try { Logger.LogStartExecutingMethod(nameof(GetDirectoryAsync)); + virtualPath = PathUtilities.NormalizeVirtualPath(virtualPath); + var prefix = PathUtilities.GetPrefix(virtualPath); var adapter = GetAdapter(prefix); - adapter.Connect(); + await adapter.ConnectAsync(cancellationToken); if (!adapter.AdapterConfiguration.EnableCache) { adapter.ClearCache(); } - return adapter.GetDirectoryAsync(virtualPath, cancellationToken); + return await adapter.GetDirectoryAsync(virtualPath, cancellationToken); } catch (FileSystemException fileSystemException) { @@ -272,23 +276,25 @@ public IEnumerable GetFiles(string virtualPath = "") /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. /// Thrown if the directory does not exists at the given path. - public Task> GetFilesAsync(string virtualPath = "", CancellationToken cancellationToken = default) + public async Task> GetFilesAsync(string virtualPath = "", CancellationToken cancellationToken = default) { try { Logger.LogStartExecutingMethod(nameof(GetFilesAsync)); + virtualPath = PathUtilities.NormalizeVirtualPath(virtualPath); + var prefix = PathUtilities.GetPrefix(virtualPath); var adapter = GetAdapter(prefix); - adapter.Connect(); + await adapter.ConnectAsync(cancellationToken); if (!adapter.AdapterConfiguration.EnableCache) { adapter.ClearCache(); } - return adapter.GetFilesAsync(virtualPath, cancellationToken); + return await adapter.GetFilesAsync(virtualPath, cancellationToken); } catch (FileSystemException fileSystemException) { @@ -331,23 +337,25 @@ public IEnumerable GetDirectories(string virtualPath = "") /// Thrown when an adapter could not be found via the provided prefix. /// Thrown when a prefix in the provided path could not be found. /// Thrown if the directory does not exists at the given path. - public Task> GetDirectoriesAsync(string virtualPath = "", CancellationToken cancellationToken = default) + public async Task> GetDirectoriesAsync(string virtualPath = "", CancellationToken cancellationToken = default) { try { Logger.LogStartExecutingMethod(nameof(GetDirectoriesAsync)); + virtualPath = PathUtilities.NormalizeVirtualPath(virtualPath); + var prefix = PathUtilities.GetPrefix(virtualPath); var adapter = GetAdapter(prefix); - adapter.Connect(); + await adapter.ConnectAsync(cancellationToken); if (!adapter.AdapterConfiguration.EnableCache) { adapter.ClearCache(); } - return adapter.GetDirectoriesAsync(virtualPath, cancellationToken); + return await adapter.GetDirectoriesAsync(virtualPath, cancellationToken); } catch (FileSystemException fileSystemException) { @@ -394,10 +402,12 @@ public async Task FileExistsAsync(string virtualPath, CancellationToken ca { Logger.LogStartExecutingMethod(nameof(FileExistsAsync)); + virtualPath = PathUtilities.NormalizeVirtualPath(virtualPath); + var prefix = PathUtilities.GetPrefix(virtualPath); var adapter = GetAdapter(prefix); - adapter.Connect(); + await adapter.ConnectAsync(cancellationToken); if (!adapter.AdapterConfiguration.EnableCache) { @@ -451,10 +461,12 @@ public async Task DirectoryExistsAsync(string virtualPath, CancellationTok { Logger.LogStartExecutingMethod(nameof(DirectoryExistsAsync)); + virtualPath = PathUtilities.NormalizeVirtualPath(virtualPath); + var prefix = PathUtilities.GetPrefix(virtualPath); var adapter = GetAdapter(prefix); - adapter.Connect(); + await adapter.ConnectAsync(cancellationToken); if (!adapter.AdapterConfiguration.EnableCache) { @@ -508,10 +520,12 @@ public async Task CreateDirectoryAsync(string virtualPath, CancellationToken can { Logger.LogStartExecutingMethod(nameof(CreateDirectoryAsync)); + virtualPath = PathUtilities.NormalizeVirtualPath(virtualPath); + var prefix = PathUtilities.GetPrefix(virtualPath); var adapter = GetAdapter(prefix); - adapter.Connect(); + await adapter.ConnectAsync(cancellationToken); if (!adapter.AdapterConfiguration.EnableCache) { @@ -565,10 +579,12 @@ public async Task DeleteFileAsync(string virtualPath, CancellationToken cancella { Logger.LogStartExecutingMethod(nameof(DeleteFileAsync)); + virtualPath = PathUtilities.NormalizeVirtualPath(virtualPath); + var prefix = PathUtilities.GetPrefix(virtualPath); var adapter = GetAdapter(prefix); - adapter.Connect(); + await adapter.ConnectAsync(cancellationToken); if (!adapter.AdapterConfiguration.EnableCache) { @@ -622,10 +638,12 @@ public async Task DeleteDirectoryAsync(string virtualPath, CancellationToken can { Logger.LogStartExecutingMethod(nameof(DeleteDirectoryAsync)); + virtualPath = PathUtilities.NormalizeVirtualPath(virtualPath); + var prefix = PathUtilities.GetPrefix(virtualPath); var adapter = GetAdapter(prefix); - adapter.Connect(); + await adapter.ConnectAsync(cancellationToken); if (!adapter.AdapterConfiguration.EnableCache) { @@ -663,10 +681,12 @@ public async Task ReadFileStreamAsync(string virtualPath, CancellationTo { Logger.LogStartExecutingMethod(nameof(ReadFileStreamAsync)); + virtualPath = PathUtilities.NormalizeVirtualPath(virtualPath); + var prefix = PathUtilities.GetPrefix(virtualPath); var adapter = GetAdapter(prefix); - adapter.Connect(); + await adapter.ConnectAsync(cancellationToken); if (!adapter.AdapterConfiguration.EnableCache) { @@ -722,6 +742,8 @@ public async Task ReadFileAsync(string virtualPath, CancellationToken ca { Logger.LogStartExecutingMethod(nameof(ReadFileAsync)); + virtualPath = PathUtilities.NormalizeVirtualPath(virtualPath); + var stream = await ReadFileStreamAsync(virtualPath, cancellationToken); using var memoryStream = await StreamUtilities.CopyContentsToMemoryStreamAsync(stream, true, cancellationToken); @@ -774,6 +796,8 @@ public async Task ReadTextFileAsync(string virtualPath, CancellationToke { Logger.LogStartExecutingMethod(nameof(ReadTextFileAsync)); + virtualPath = PathUtilities.NormalizeVirtualPath(virtualPath); + using var stream = await ReadFileStreamAsync(virtualPath, cancellationToken); using var streamReader = new StreamReader(stream); @@ -830,20 +854,23 @@ public async Task CopyFileAsync(string virtualSourcePath, string virtualDestinat { Logger.LogStartExecutingMethod(nameof(CopyFileAsync)); + virtualSourcePath = PathUtilities.NormalizeVirtualPath(virtualSourcePath); + virtualDestinationPath = PathUtilities.NormalizeVirtualPath(virtualDestinationPath); + var sourcePrefix = PathUtilities.GetPrefix(virtualSourcePath); var sourceAdapter = GetAdapter(sourcePrefix); var destinationPrefix = PathUtilities.GetPrefix(virtualDestinationPath); var destinationAdapter = GetAdapter(destinationPrefix); - sourceAdapter.Connect(); + await sourceAdapter.ConnectAsync(cancellationToken); if (!sourceAdapter.AdapterConfiguration.EnableCache) { sourceAdapter.ClearCache(); } - destinationAdapter.Connect(); + await destinationAdapter.ConnectAsync(cancellationToken); if (!destinationAdapter.AdapterConfiguration.EnableCache) { @@ -904,20 +931,23 @@ public async Task MoveFileAsync(string virtualSourcePath, string virtualDestinat { Logger.LogStartExecutingMethod(nameof(MoveFileAsync)); + virtualSourcePath = PathUtilities.NormalizeVirtualPath(virtualSourcePath); + virtualDestinationPath = PathUtilities.NormalizeVirtualPath(virtualDestinationPath); + var sourcePrefix = PathUtilities.GetPrefix(virtualSourcePath); var sourceAdapter = GetAdapter(sourcePrefix); var destinationPrefix = PathUtilities.GetPrefix(virtualDestinationPath); var destinationAdapter = GetAdapter(destinationPrefix); - sourceAdapter.Connect(); + await sourceAdapter.ConnectAsync(cancellationToken); if (!sourceAdapter.AdapterConfiguration.EnableCache) { sourceAdapter.ClearCache(); } - destinationAdapter.Connect(); + await destinationAdapter.ConnectAsync(cancellationToken); if (!destinationAdapter.AdapterConfiguration.EnableCache) { @@ -961,10 +991,12 @@ public async Task WriteFileAsync(string virtualPath, Stream contents, bool overw { Logger.LogStartExecutingMethod(nameof(WriteFileAsync)); + virtualPath = PathUtilities.NormalizeVirtualPath(virtualPath); + var prefix = PathUtilities.GetPrefix(virtualPath); var adapter = GetAdapter(prefix); - adapter.Connect(); + await adapter.ConnectAsync(cancellationToken); if (!adapter.AdapterConfiguration.EnableCache) { @@ -1078,10 +1110,12 @@ public async Task AppendFileAsync(string virtualPath, Stream contents, Cancellat { Logger.LogStartExecutingMethod(nameof(AppendFileAsync)); + virtualPath = PathUtilities.NormalizeVirtualPath(virtualPath); + var prefix = PathUtilities.GetPrefix(virtualPath); var adapter = GetAdapter(prefix); - adapter.Connect(); + await adapter.ConnectAsync(cancellationToken); if (!adapter.AdapterConfiguration.EnableCache) { diff --git a/FileSystem/src/Utilities/PathUtilities.cs b/FileSystem/src/Utilities/PathUtilities.cs index 2077fb8..61a008e 100644 --- a/FileSystem/src/Utilities/PathUtilities.cs +++ b/FileSystem/src/Utilities/PathUtilities.cs @@ -36,6 +36,28 @@ public static string NormalizeRootPath(string rootPath) return rootPath.Replace(InvalidPathSeparator, PathSeparator).RemoveTrailingForwardSlash(); } + /// + /// Normalizes a virtual path. + /// + /// The virtual path. + /// The normalized virtual path. + public static string NormalizeVirtualPath(string virtualPath) + { + if (!virtualPath.Contains(AdapterPrefixSeparator)) + { + throw new InvalidVirtualPathException(virtualPath); + } + + var prefixAndPath = ResolvePrefixAndPath(virtualPath); + + if (prefixAndPath.Length == 1) + { + return virtualPath; + } + + return prefixAndPath[0] + AdapterPrefixSeparator + prefixAndPath[1].RemoveLeadingForwardSlash(); + } + /// /// Returns the path from a prefixed path. /// diff --git a/FileSystem/src/Utilities/StreamUtilities.cs b/FileSystem/src/Utilities/StreamUtilities.cs index 4462951..aba6178 100644 --- a/FileSystem/src/Utilities/StreamUtilities.cs +++ b/FileSystem/src/Utilities/StreamUtilities.cs @@ -10,7 +10,7 @@ public static class StreamUtilities public static async Task CopyContentsToMemoryStreamAsync(Stream sourceStream, bool setPositionToStart, CancellationToken cancellationToken = default) { var memoryStream = new MemoryStream(); - await sourceStream.CopyToAsync(memoryStream, AdapterConstants.DefaultMemoryStreamBufferSize, cancellationToken); + await sourceStream.CopyToAsync(memoryStream, FileSystemConstants.Streaming.DefaultMemoryStreamBufferSize, cancellationToken); if (setPositionToStart) { diff --git a/README.md b/README.md index 7eadfd1..aeb6ee4 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ For adapters other than the local file system (included in the `SharpGrip.FileSy | [AzureBlobStorage](#azureblobstorage-adapter) | `SharpGrip.FileSystem.Adapters.AzureBlobStorage` | [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem.Adapters.AzureBlobStorage)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.AzureBlobStorage) | | [AzureFileStorage](#azurefilestorage-adapter) | `SharpGrip.FileSystem.Adapters.AzureFileStorage` | [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem.Adapters.AzureFileStorage)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.AzureFileStorage) | | [Dropbox](#dropbox-adapter) | `SharpGrip.FileSystem.Adapters.Dropbox` | [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem.Adapters.Dropbox)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.Dropbox) | +| [FTP](#ftp-adapter) | `SharpGrip.FileSystem.Adapters.Ftp` | [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem.Adapters.Ftp)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.Ftp) | | [GoogleDrive](#googledrive-adapter) | `SharpGrip.FileSystem.Adapters.GoogleDrive` | [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem.Adapters.GoogleDrive)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.GoogleDrive) | | [MicrosoftOneDrive](#microsoftonedrive-adapter) | `SharpGrip.FileSystem.Adapters.MicrosoftOneDrive` | [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem.Adapters.MicrosoftOneDrive)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.MicrosoftOneDrive) | | [SFTP](#sftp-adapter) | `SharpGrip.FileSystem.Adapters.Sftp` | [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem.Adapters.Sftp)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.Sftp) | @@ -128,6 +129,21 @@ var adapters = new List var fileSystem = new FileSystem(adapters); ``` +### FTP adapter + +``` +// FTP connection. +var ftpClient = new AsyncFtpClient("hostname", "username", "password"); + +var adapters = new List +{ + new LocalAdapter("local", "/var/files"), + new FtpAdapter("ftp", "/var/files", ftpClient) +}; + +var fileSystem = new FileSystem(adapters); +``` + ### GoogleDrive adapter ``` diff --git a/Tests/src/FileSystem.Adapters.AmazonS3/AmazonS3AdapterTest.cs b/Tests/src/FileSystem.Adapters.AmazonS3/AmazonS3AdapterTest.cs index 0342332..45f1a59 100644 --- a/Tests/src/FileSystem.Adapters.AmazonS3/AmazonS3AdapterTest.cs +++ b/Tests/src/FileSystem.Adapters.AmazonS3/AmazonS3AdapterTest.cs @@ -36,14 +36,12 @@ public void Test_Instantiation() } [Fact] - public Task Test_Connect() + public async Task Test_Connect() { var amazonS3Client = Substitute.For(); var amazonS3Adapter = new AmazonS3Adapter("prefix", "/root-path", amazonS3Client, "bucket"); - amazonS3Adapter.Connect(); - - return Task.CompletedTask; + await amazonS3Adapter.ConnectAsync(); } [Fact] diff --git a/Tests/src/FileSystem.Adapters.AzureBlobStorage/AzureBlobStorageAdapterTest.cs b/Tests/src/FileSystem.Adapters.AzureBlobStorage/AzureBlobStorageAdapterTest.cs index 38e18a5..b57bc9a 100644 --- a/Tests/src/FileSystem.Adapters.AzureBlobStorage/AzureBlobStorageAdapterTest.cs +++ b/Tests/src/FileSystem.Adapters.AzureBlobStorage/AzureBlobStorageAdapterTest.cs @@ -19,14 +19,12 @@ public void Test_Instantiation() } [Fact] - public Task Test_Connect() + public async Task Test_Connect() { var blobContainerClient = Substitute.For(); var azureBlobStorageAdapter = new AzureBlobStorageAdapter("prefix", "/root-path", blobContainerClient); - azureBlobStorageAdapter.Connect(); - - return Task.CompletedTask; + await azureBlobStorageAdapter.ConnectAsync(); } [Fact] diff --git a/Tests/src/FileSystem.Adapters.AzureFileStorage/AzureFileStorageAdapterTest.cs b/Tests/src/FileSystem.Adapters.AzureFileStorage/AzureFileStorageAdapterTest.cs index 3dedba0..9ada96b 100644 --- a/Tests/src/FileSystem.Adapters.AzureFileStorage/AzureFileStorageAdapterTest.cs +++ b/Tests/src/FileSystem.Adapters.AzureFileStorage/AzureFileStorageAdapterTest.cs @@ -19,14 +19,12 @@ public void Test_Instantiation() } [Fact] - public Task Test_Connect() + public async Task Test_Connect() { var shareClient = Substitute.For(); var azureFileStorageAdapter = new AzureFileStorageAdapter("prefix", "/root-path", shareClient); - azureFileStorageAdapter.Connect(); - - return Task.CompletedTask; + await azureFileStorageAdapter.ConnectAsync(); } [Fact] diff --git a/Tests/src/FileSystem.Adapters.GoogleDrive/GoogleDriveAdapterTest.cs b/Tests/src/FileSystem.Adapters.GoogleDrive/GoogleDriveAdapterTest.cs index c19ea3a..ed76597 100644 --- a/Tests/src/FileSystem.Adapters.GoogleDrive/GoogleDriveAdapterTest.cs +++ b/Tests/src/FileSystem.Adapters.GoogleDrive/GoogleDriveAdapterTest.cs @@ -19,14 +19,12 @@ public void Test_Instantiation() } [Fact] - public Task Test_Connect() + public async Task Test_Connect() { var googleDriveClient = Substitute.For(); var googleDriveAdapter = new GoogleDriveAdapter("prefix", "/root-path", googleDriveClient); - googleDriveAdapter.Connect(); - - return Task.CompletedTask; + await googleDriveAdapter.ConnectAsync(); } [Fact] diff --git a/Tests/src/FileSystem.Adapters.MicrosoftOneDrive/MicrosoftOneDriveAdapterTest.cs b/Tests/src/FileSystem.Adapters.MicrosoftOneDrive/MicrosoftOneDriveAdapterTest.cs index e6cc23d..78072aa 100644 --- a/Tests/src/FileSystem.Adapters.MicrosoftOneDrive/MicrosoftOneDriveAdapterTest.cs +++ b/Tests/src/FileSystem.Adapters.MicrosoftOneDrive/MicrosoftOneDriveAdapterTest.cs @@ -21,15 +21,13 @@ public void Test_Instantiation() } [Fact] - public Task Test_Connect() + public async Task Test_Connect() { var delegateAuthenticationProvider = new DelegateAuthenticationProvider(message => Task.FromResult(message.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "12345"))); var graphServiceClient = Substitute.For(delegateAuthenticationProvider, null); var microsoftOneDriveAdapter = new MicrosoftOneDriveAdapter("prefix", "/root-path", graphServiceClient, "driveId"); - microsoftOneDriveAdapter.Connect(); - - return Task.CompletedTask; + await microsoftOneDriveAdapter.ConnectAsync(); } [Fact] diff --git a/Tests/src/FileSystem.Adapters.Sftp/SftpAdapterTest.cs b/Tests/src/FileSystem.Adapters.Sftp/SftpAdapterTest.cs index d410219..df4b1a4 100644 --- a/Tests/src/FileSystem.Adapters.Sftp/SftpAdapterTest.cs +++ b/Tests/src/FileSystem.Adapters.Sftp/SftpAdapterTest.cs @@ -31,14 +31,12 @@ public void Test_Instantiation() } [Fact] - public Task Test_Connect() + public async Task Test_Connect() { var sftpClient = Substitute.For(); var sftpAdapter = new SftpAdapter("prefix", "/root-path", sftpClient); - sftpAdapter.Connect(); - - return Task.CompletedTask; + await sftpAdapter.ConnectAsync(); } [Fact] From 8c73c61386ba9c0de1b4c24f4fef96af5aca8f7a Mon Sep 17 00:00:00 2001 From: Mauro van der Gun Date: Tue, 2 Jan 2024 10:26:21 -0400 Subject: [PATCH 2/8] remove unnecessary type cast --- FileSystem.Adapters.Sftp/src/SftpAdapter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FileSystem.Adapters.Sftp/src/SftpAdapter.cs b/FileSystem.Adapters.Sftp/src/SftpAdapter.cs index dc9e6ce..071332a 100644 --- a/FileSystem.Adapters.Sftp/src/SftpAdapter.cs +++ b/FileSystem.Adapters.Sftp/src/SftpAdapter.cs @@ -32,7 +32,7 @@ public SftpAdapter(string prefix, string rootPath, ISftpClient client, Action Date: Sun, 14 Jan 2024 22:34:33 -0400 Subject: [PATCH 3/8] implement remaining methods --- .../src/GoogleCloudStorageAdapter.cs | 139 +++++++++++++++--- .../src/ModelFactory.cs | 3 +- 2 files changed, 121 insertions(+), 21 deletions(-) diff --git a/FileSystem.Adapters.GoogleCloudStorage/src/GoogleCloudStorageAdapter.cs b/FileSystem.Adapters.GoogleCloudStorage/src/GoogleCloudStorageAdapter.cs index 6253422..f8f2d62 100644 --- a/FileSystem.Adapters.GoogleCloudStorage/src/GoogleCloudStorageAdapter.cs +++ b/FileSystem.Adapters.GoogleCloudStorage/src/GoogleCloudStorageAdapter.cs @@ -1,14 +1,19 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; +using System.Net; using System.Threading; using System.Threading.Tasks; +using Google; using Google.Cloud.Storage.V1; using SharpGrip.FileSystem.Exceptions; using SharpGrip.FileSystem.Extensions; using SharpGrip.FileSystem.Models; +using SharpGrip.FileSystem.Utilities; using DirectoryNotFoundException = SharpGrip.FileSystem.Exceptions.DirectoryNotFoundException; using FileNotFoundException = SharpGrip.FileSystem.Exceptions.FileNotFoundException; +using Object = Google.Apis.Storage.v1.Data.Object; namespace SharpGrip.FileSystem.Adapters.GoogleCloudStorage { @@ -51,6 +56,10 @@ public override async Task GetFileAsync(string virtualPath, CancellationT return ModelFactory.CreateFile(file, path, virtualPath); } + catch (GoogleApiException googleApiException) when (googleApiException.HttpStatusCode == HttpStatusCode.NotFound) + { + throw new FileNotFoundException(path, Prefix); + } catch (Exception exception) { throw Exception(exception); @@ -59,11 +68,16 @@ public override async Task GetFileAsync(string virtualPath, CancellationT public override async Task GetDirectoryAsync(string virtualPath, CancellationToken cancellationToken = default) { - var path = GetPath(virtualPath).EnsureLeadingForwardSlash().EnsureTrailingForwardSlash(); + var path = GetPath(virtualPath).RemoveLeadingForwardSlash().EnsureTrailingForwardSlash(); var parentPath = GetParentPathPart(path).EnsureTrailingForwardSlash(); try { + if (path.IsNullOrEmpty() || path == "/") + { + return ModelFactory.CreateDirectory("/", path, virtualPath); + } + var request = client.Service.Objects.List(bucketName); request.Prefix = parentPath == "/" ? null : parentPath; @@ -73,13 +87,16 @@ public override async Task GetDirectoryAsync(string virtualPath, Can { var objects = await request.ExecuteAsync(cancellationToken: cancellationToken); - foreach (var directoryName in objects.Prefixes) + if (objects.Prefixes != null) { - var directoryPath = parentPath + directoryName; - - if (directoryPath == path) + foreach (var directoryPath in objects.Prefixes) { - return ModelFactory.CreateDirectory(directoryName.RemoveTrailingForwardSlash(), directoryPath, GetVirtualPath(directoryPath)); + if (directoryPath == path) + { + var directoryName = GetLastPathPart(directoryPath); + + return ModelFactory.CreateDirectory(directoryName.RemoveTrailingForwardSlash(), directoryPath.EnsureTrailingForwardSlash(), GetVirtualPath(directoryPath)); + } } } @@ -88,6 +105,10 @@ public override async Task GetDirectoryAsync(string virtualPath, Can throw new DirectoryNotFoundException(path, Prefix); } + catch (GoogleApiException googleApiException) when (googleApiException.HttpStatusCode == HttpStatusCode.NotFound) + { + throw new DirectoryNotFoundException(path, Prefix); + } catch (Exception exception) { throw Exception(exception); @@ -113,11 +134,12 @@ public override async Task> GetFilesAsync(string virtualPath { var objects = await request.ExecuteAsync(cancellationToken: cancellationToken); - foreach (var file in objects.Items) + if (objects.Items != null) { - var filePath = path + file.Name; - - files.Add(ModelFactory.CreateFile(file, filePath.RemoveTrailingForwardSlash(), GetVirtualPath(filePath))); + foreach (var file in objects.Items.Where(item => item.ContentType != null)) + { + files.Add(ModelFactory.CreateFile(file, file.Name.RemoveTrailingForwardSlash(), GetVirtualPath(file.Name))); + } } request.PageToken = objects.NextPageToken; @@ -150,11 +172,14 @@ public override async Task> GetDirectoriesAsync(string v { var objects = await request.ExecuteAsync(cancellationToken: cancellationToken); - foreach (var directoryName in objects.Prefixes) + if (objects.Prefixes != null) { - var directoryPath = path + directoryName; + foreach (var directoryPath in objects.Prefixes) + { + var directoryName = GetLastPathPart(directoryPath); - directories.Add(ModelFactory.CreateDirectory(directoryName.RemoveTrailingForwardSlash(), directoryPath.EnsureTrailingForwardSlash(), GetVirtualPath(directoryPath))); + directories.Add(ModelFactory.CreateDirectory(directoryName.RemoveTrailingForwardSlash(), directoryPath.EnsureTrailingForwardSlash(), GetVirtualPath(directoryPath))); + } } request.PageToken = objects.NextPageToken; @@ -179,9 +204,7 @@ public override async Task CreateDirectoryAsync(string virtualPath, Cancellation try { - // client.Service. - - await client.UploadObjectAsync(bucketName, GetLastPathPart(path).EnsureTrailingForwardSlash(), null, Stream.Null, cancellationToken: cancellationToken); + await client.UploadObjectAsync(bucketName, path.EnsureTrailingForwardSlash(), null, Stream.Null, cancellationToken: cancellationToken); } catch (Exception exception) { @@ -191,22 +214,98 @@ public override async Task CreateDirectoryAsync(string virtualPath, Cancellation public override async Task DeleteDirectoryAsync(string virtualPath, CancellationToken cancellationToken = default) { - throw new NotImplementedException(); + await GetDirectoryAsync(virtualPath, cancellationToken); + + var path = GetPath(virtualPath).RemoveLeadingForwardSlash().EnsureTrailingForwardSlash(); + + try + { + var files = await GetFilesAsync(virtualPath, cancellationToken); + + foreach (var file in files) + { + await DeleteFileAsync(file.VirtualPath, cancellationToken); + } + + var subDirectories = await GetDirectoriesAsync(virtualPath, cancellationToken); + + foreach (var subDirectory in subDirectories) + { + await DeleteDirectoryAsync(subDirectory.VirtualPath, cancellationToken); + } + + await client.DeleteObjectAsync(bucketName, path, cancellationToken: cancellationToken); + } + catch (Exception exception) + { + throw Exception(exception); + } } public override async Task DeleteFileAsync(string virtualPath, CancellationToken cancellationToken = default) { - throw new NotImplementedException(); + await GetFileAsync(virtualPath, cancellationToken); + + var path = GetPath(virtualPath).RemoveLeadingForwardSlash().RemoveTrailingForwardSlash(); + + try + { + await client.DeleteObjectAsync(bucketName, path, cancellationToken: cancellationToken); + } + catch (Exception exception) + { + throw Exception(exception); + } } public override async Task ReadFileStreamAsync(string virtualPath, CancellationToken cancellationToken = default) { - throw new NotImplementedException(); + await GetFileAsync(virtualPath, cancellationToken); + + var path = GetPath(virtualPath).RemoveLeadingForwardSlash().RemoveTrailingForwardSlash(); + + try + { + var file = await client.GetObjectAsync(bucketName, path, new GetObjectOptions(), cancellationToken); + + var memoryStream = new MemoryStream(); + + await client.DownloadObjectAsync(file, memoryStream, new DownloadObjectOptions(), cancellationToken); + + memoryStream.Position = 0; + + return memoryStream; + } + catch (Exception exception) + { + throw Exception(exception); + } } public override async Task WriteFileAsync(string virtualPath, Stream contents, bool overwrite = false, CancellationToken cancellationToken = default) { - throw new NotImplementedException(); + if (!overwrite && await FileExistsAsync(virtualPath, cancellationToken)) + { + throw new FileExistsException(GetPath(virtualPath), Prefix); + } + + var path = GetPath(virtualPath).RemoveLeadingForwardSlash().RemoveTrailingForwardSlash(); + + try + { + var file = new Object + { + Bucket = bucketName, + Name = path, + ContentType = ContentTypeProvider.GetContentType(path) + }; + + await client.UploadObjectAsync(file, contents, new UploadObjectOptions(), cancellationToken); + } + catch (Exception exception) + { + throw Exception(exception); + } } protected override Exception Exception(Exception exception) diff --git a/FileSystem.Adapters.GoogleCloudStorage/src/ModelFactory.cs b/FileSystem.Adapters.GoogleCloudStorage/src/ModelFactory.cs index c016e50..6a49dab 100644 --- a/FileSystem.Adapters.GoogleCloudStorage/src/ModelFactory.cs +++ b/FileSystem.Adapters.GoogleCloudStorage/src/ModelFactory.cs @@ -1,5 +1,6 @@ using Google.Apis.Storage.v1.Data; using SharpGrip.FileSystem.Models; +using SharpGrip.FileSystem.Utilities; namespace SharpGrip.FileSystem.Adapters.GoogleCloudStorage { @@ -9,7 +10,7 @@ public static FileModel CreateFile(Object file, string path, string virtualPath) { return new FileModel { - Name = file.Name, + Name = PathUtilities.GetLastPathPart(file.Name), Path = path, VirtualPath = virtualPath, Length = (long?) file.Size, From f76508fd4d2a175f8646543ab5491c4a349ac5cb Mon Sep 17 00:00:00 2001 From: Mauro van der Gun Date: Sun, 14 Jan 2024 22:51:30 -0400 Subject: [PATCH 4/8] add google cloud storage adapter documentation --- README.md | 44 ++++++++++++++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index cc7688a..656a5d2 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,10 @@ ## Introduction -SharpGrip FileSystem is a .NET file system abstraction supporting multiple adapters. +SharpGrip FileSystem is a versatile .NET file system abstraction that supports multiple storage adapters. +It empowers developers to manage various file systems and services through a unified and easily comprehensible API. +By coding against the abstractions provided by this library, developers can sidestep vendor-specific APIs, effectively avoiding vendor lock-ins. +This flexibility enhances the portability and maintainability of the codebase, allowing for smoother transitions between different file systems. ## Installation @@ -22,17 +25,18 @@ For adapters other than the local file system (included in the `SharpGrip.FileSy ## Supported adapters -| Adapter | Package | NuGet | -|:------------------------------------------------|:--------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| [Local adapter](#local-adapter) | `SharpGrip.FileSystem` | [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem)](https://www.nuget.org/packages/SharpGrip.FileSystem) [![NuGet](https://img.shields.io/nuget/dt/SharpGrip.FileSystem)](https://www.nuget.org/packages/SharpGrip.FileSystem) | -| [AmazonS3](#amazons3-adapter) | `SharpGrip.FileSystem.Adapters.AmazonS3` | [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem.Adapters.AmazonS3)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.AmazonS3) [![NuGet](https://img.shields.io/nuget/dt/SharpGrip.FileSystem.Adapters.AmazonS3)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.AmazonS3) | -| [AzureBlobStorage](#azureblobstorage-adapter) | `SharpGrip.FileSystem.Adapters.AzureBlobStorage` | [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem.Adapters.AzureBlobStorage)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.AzureBlobStorage) [![NuGet](https://img.shields.io/nuget/dt/SharpGrip.FileSystem.Adapters.AzureBlobStorage)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.AzureBlobStorage) | -| [AzureFileStorage](#azurefilestorage-adapter) | `SharpGrip.FileSystem.Adapters.AzureFileStorage` | [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem.Adapters.AzureFileStorage)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.AzureFileStorage) [![NuGet](https://img.shields.io/nuget/dt/SharpGrip.FileSystem.Adapters.AzureFileStorage)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.AzureFileStorage) | -| [Dropbox](#dropbox-adapter) | `SharpGrip.FileSystem.Adapters.Dropbox` | [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem.Adapters.Dropbox)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.Dropbox) [![NuGet](https://img.shields.io/nuget/dt/SharpGrip.FileSystem.Adapters.Dropbox)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.Dropbox) | -| [FTP](#ftp-adapter) | `SharpGrip.FileSystem.Adapters.Ftp` | [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem.Adapters.Ftp)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.Ftp) [![NuGet](https://img.shields.io/nuget/dt/SharpGrip.FileSystem.Adapters.Ftp)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.Ftp) | -| [GoogleDrive](#googledrive-adapter) | `SharpGrip.FileSystem.Adapters.GoogleDrive` | [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem.Adapters.GoogleDrive)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.GoogleDrive) [![NuGet](https://img.shields.io/nuget/dt/SharpGrip.FileSystem.Adapters.GoogleDrive)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.GoogleDrive) | -| [MicrosoftOneDrive](#microsoftonedrive-adapter) | `SharpGrip.FileSystem.Adapters.MicrosoftOneDrive` | [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem.Adapters.MicrosoftOneDrive)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.MicrosoftOneDrive) [![NuGet](https://img.shields.io/nuget/dt/SharpGrip.FileSystem.Adapters.MicrosoftOneDrive)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.MicrosoftOneDrive) | -| [SFTP](#sftp-adapter) | `SharpGrip.FileSystem.Adapters.Sftp` | [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem.Adapters.Sftp)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.Sftp) [![NuGet](https://img.shields.io/nuget/dt/SharpGrip.FileSystem.Adapters.Sftp)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.Sftp) | +| Adapter | Package | NuGet | +|:--------------------------------------------------|:---------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| [Local adapter](#local-adapter) | `SharpGrip.FileSystem` | [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem)](https://www.nuget.org/packages/SharpGrip.FileSystem) [![NuGet](https://img.shields.io/nuget/dt/SharpGrip.FileSystem)](https://www.nuget.org/packages/SharpGrip.FileSystem) | +| [AmazonS3](#amazons3-adapter) | `SharpGrip.FileSystem.Adapters.AmazonS3` | [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem.Adapters.AmazonS3)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.AmazonS3) [![NuGet](https://img.shields.io/nuget/dt/SharpGrip.FileSystem.Adapters.AmazonS3)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.AmazonS3) | +| [AzureBlobStorage](#azureblobstorage-adapter) | `SharpGrip.FileSystem.Adapters.AzureBlobStorage` | [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem.Adapters.AzureBlobStorage)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.AzureBlobStorage) [![NuGet](https://img.shields.io/nuget/dt/SharpGrip.FileSystem.Adapters.AzureBlobStorage)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.AzureBlobStorage) | +| [AzureFileStorage](#azurefilestorage-adapter) | `SharpGrip.FileSystem.Adapters.AzureFileStorage` | [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem.Adapters.AzureFileStorage)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.AzureFileStorage) [![NuGet](https://img.shields.io/nuget/dt/SharpGrip.FileSystem.Adapters.AzureFileStorage)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.AzureFileStorage) | +| [Dropbox](#dropbox-adapter) | `SharpGrip.FileSystem.Adapters.Dropbox` | [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem.Adapters.Dropbox)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.Dropbox) [![NuGet](https://img.shields.io/nuget/dt/SharpGrip.FileSystem.Adapters.Dropbox)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.Dropbox) | +| [FTP](#ftp-adapter) | `SharpGrip.FileSystem.Adapters.Ftp` | [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem.Adapters.Ftp)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.Ftp) [![NuGet](https://img.shields.io/nuget/dt/SharpGrip.FileSystem.Adapters.Ftp)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.Ftp) | +| [GoogleCloudStorage](#googlecloudstorage-adapter) | `SharpGrip.FileSystem.Adapters.GoogleCloudStorage` | [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem.Adapters.GoogleCloudStorage)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.GoogleCloudStorage) [![NuGet](https://img.shields.io/nuget/dt/SharpGrip.FileSystem.Adapters.GoogleCloudStorage)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.GoogleCloudStorage) | +| [GoogleDrive](#googledrive-adapter) | `SharpGrip.FileSystem.Adapters.GoogleDrive` | [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem.Adapters.GoogleDrive)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.GoogleDrive) [![NuGet](https://img.shields.io/nuget/dt/SharpGrip.FileSystem.Adapters.GoogleDrive)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.GoogleDrive) | +| [MicrosoftOneDrive](#microsoftonedrive-adapter) | `SharpGrip.FileSystem.Adapters.MicrosoftOneDrive` | [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem.Adapters.MicrosoftOneDrive)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.MicrosoftOneDrive) [![NuGet](https://img.shields.io/nuget/dt/SharpGrip.FileSystem.Adapters.MicrosoftOneDrive)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.MicrosoftOneDrive) | +| [SFTP](#sftp-adapter) | `SharpGrip.FileSystem.Adapters.Sftp` | [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem.Adapters.Sftp)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.Sftp) [![NuGet](https://img.shields.io/nuget/dt/SharpGrip.FileSystem.Adapters.Sftp)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.Sftp) | ## Supported operations @@ -144,6 +148,22 @@ var adapters = new List var fileSystem = new FileSystem(adapters); ``` +### GoogleCloudStorage adapter + +``` +// Google connection. +var credential = GoogleCredential.FromFile("path/to/credential/file"); +var storageClient = await StorageClient.CreateAsync(credential); + +var adapters = new List +{ + new LocalAdapter("local", "/var/files"), + new GoogleCloudStorageAdapter(prefix, rootPath, storageClient, "bucketName", configuration); +}; + +var fileSystem = new FileSystem(adapters); +``` + ### GoogleDrive adapter ``` From 9e0dbec27207d77bc207d9246cf798cc6e6850fb Mon Sep 17 00:00:00 2001 From: Mauro van der Gun Date: Mon, 12 Feb 2024 14:38:23 -0400 Subject: [PATCH 5/8] split up readme files per adapter --- .../FileSystem.Adapters.AmazonS3.csproj | 2 +- FileSystem.Adapters.AmazonS3/README.md | 20 ++ ...ileSystem.Adapters.AzureBlobStorage.csproj | 2 +- .../README.md | 21 ++ ...ileSystem.Adapters.AzureFileStorage.csproj | 2 +- .../README.md | 20 ++ .../FileSystem.Adapters.Dropbox.csproj | 2 +- FileSystem.Adapters.Dropbox/README.md | 20 ++ .../FileSystem.Adapters.Ftp.csproj | 2 +- FileSystem.Adapters.Ftp/README.md | 20 ++ ...eSystem.Adapters.GoogleCloudStorage.csproj | 2 +- .../README.md | 21 ++ .../FileSystem.Adapters.GoogleDrive.csproj | 2 +- FileSystem.Adapters.GoogleDrive/README.md | 33 +++ ...leSystem.Adapters.MicrosoftOneDrive.csproj | 2 +- .../README.md | 32 +++ .../FileSystem.Adapters.Sftp.csproj | 2 +- FileSystem.Adapters.Sftp/README.md | 23 +++ README.md | 195 ++---------------- 19 files changed, 234 insertions(+), 189 deletions(-) create mode 100644 FileSystem.Adapters.AmazonS3/README.md create mode 100644 FileSystem.Adapters.AzureBlobStorage/README.md create mode 100644 FileSystem.Adapters.AzureFileStorage/README.md create mode 100644 FileSystem.Adapters.Dropbox/README.md create mode 100644 FileSystem.Adapters.Ftp/README.md create mode 100644 FileSystem.Adapters.GoogleCloudStorage/README.md create mode 100644 FileSystem.Adapters.GoogleDrive/README.md create mode 100644 FileSystem.Adapters.MicrosoftOneDrive/README.md create mode 100644 FileSystem.Adapters.Sftp/README.md diff --git a/FileSystem.Adapters.AmazonS3/FileSystem.Adapters.AmazonS3.csproj b/FileSystem.Adapters.AmazonS3/FileSystem.Adapters.AmazonS3.csproj index d5056a1..6e8d34d 100644 --- a/FileSystem.Adapters.AmazonS3/FileSystem.Adapters.AmazonS3.csproj +++ b/FileSystem.Adapters.AmazonS3/FileSystem.Adapters.AmazonS3.csproj @@ -13,7 +13,7 @@ - + diff --git a/FileSystem.Adapters.AmazonS3/README.md b/FileSystem.Adapters.AmazonS3/README.md new file mode 100644 index 0000000..37a8126 --- /dev/null +++ b/FileSystem.Adapters.AmazonS3/README.md @@ -0,0 +1,20 @@ +# SharpGrip FileSystem AmazonS3 adapter [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem.Adapters.AmazonS3)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.AmazonS3) + +## Installation + +Reference NuGet package `SharpGrip.FileSystem.Adapters.AmazonS3` (https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.AmazonS3). + +## Usage + +``` +// Amazon connection. +var amazonClient = new AmazonS3Client("awsAccessKeyId", "awsSecretAccessKey", RegionEndpoint.USEast2); + +var adapters = new List +{ + new AmazonS3Adapter("amazon1", "/Files", amazonClient, "bucketName1") + new AmazonS3Adapter("amazon2", "/Files", amazonClient, "bucketName2") +}; + +var fileSystem = new FileSystem(adapters); +``` \ No newline at end of file diff --git a/FileSystem.Adapters.AzureBlobStorage/FileSystem.Adapters.AzureBlobStorage.csproj b/FileSystem.Adapters.AzureBlobStorage/FileSystem.Adapters.AzureBlobStorage.csproj index ca2ba34..9db2cc1 100644 --- a/FileSystem.Adapters.AzureBlobStorage/FileSystem.Adapters.AzureBlobStorage.csproj +++ b/FileSystem.Adapters.AzureBlobStorage/FileSystem.Adapters.AzureBlobStorage.csproj @@ -13,7 +13,7 @@ - + diff --git a/FileSystem.Adapters.AzureBlobStorage/README.md b/FileSystem.Adapters.AzureBlobStorage/README.md new file mode 100644 index 0000000..c2f3288 --- /dev/null +++ b/FileSystem.Adapters.AzureBlobStorage/README.md @@ -0,0 +1,21 @@ +# SharpGrip FileSystem AzureBlobStorage adapter [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem.Adapters.AzureBlobStorage)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.AzureBlobStorage) + +## Installation + +Reference NuGet package `SharpGrip.FileSystem.Adapters.AzureBlobStorage` (https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.AzureBlobStorage). + +## Usage + +``` +// Azure connection. +var blobServiceClient = new BlobServiceClient("connectionString"); +var azureClient = blobServiceClient.GetBlobContainerClient("blobContainerName"); + +var adapters = new List +{ + new LocalAdapter("local", "/var/files"), + new AzureBlobStorageAdapter("azure", "/Files", azureClient) +}; + +var fileSystem = new FileSystem(adapters); +``` \ No newline at end of file diff --git a/FileSystem.Adapters.AzureFileStorage/FileSystem.Adapters.AzureFileStorage.csproj b/FileSystem.Adapters.AzureFileStorage/FileSystem.Adapters.AzureFileStorage.csproj index 94c929f..bc11517 100644 --- a/FileSystem.Adapters.AzureFileStorage/FileSystem.Adapters.AzureFileStorage.csproj +++ b/FileSystem.Adapters.AzureFileStorage/FileSystem.Adapters.AzureFileStorage.csproj @@ -13,7 +13,7 @@ - + diff --git a/FileSystem.Adapters.AzureFileStorage/README.md b/FileSystem.Adapters.AzureFileStorage/README.md new file mode 100644 index 0000000..e8012f0 --- /dev/null +++ b/FileSystem.Adapters.AzureFileStorage/README.md @@ -0,0 +1,20 @@ +# SharpGrip FileSystem AzureFileStorage adapter [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem.Adapters.AzureFileStorage)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.AzureFileStorage) + +## Installation + +Reference NuGet package `SharpGrip.FileSystem.Adapters.AzureFileStorage` (https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.AzureFileStorage). + +## Usage + +``` +// Azure connection. +var azureClient = new ShareClient("connectionString", "shareName"); + +var adapters = new List +{ + new LocalAdapter("local", "/var/files"), + new AzureFileStorageAdapter("azure", "/Files", azureClient) +}; + +var fileSystem = new FileSystem(adapters); +``` \ No newline at end of file diff --git a/FileSystem.Adapters.Dropbox/FileSystem.Adapters.Dropbox.csproj b/FileSystem.Adapters.Dropbox/FileSystem.Adapters.Dropbox.csproj index cec4561..da51d91 100644 --- a/FileSystem.Adapters.Dropbox/FileSystem.Adapters.Dropbox.csproj +++ b/FileSystem.Adapters.Dropbox/FileSystem.Adapters.Dropbox.csproj @@ -13,7 +13,7 @@ - + diff --git a/FileSystem.Adapters.Dropbox/README.md b/FileSystem.Adapters.Dropbox/README.md new file mode 100644 index 0000000..db09f84 --- /dev/null +++ b/FileSystem.Adapters.Dropbox/README.md @@ -0,0 +1,20 @@ +# SharpGrip FileSystem Dropbox adapter [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem.Adapters.Dropbox)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.Dropbox) + +## Installation + +Reference NuGet package `SharpGrip.FileSystem.Adapters.Dropbox` (https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.Dropbox). + +## Usage + +``` +// Dropbox connection. +var dropboxClient = new DropboxClient("oAuth2AccessToken"); + +var adapters = new List +{ + new LocalAdapter("local", "/var/files"), + new DropboxAdapter("dropbox", "/Files", dropboxClient) +}; + +var fileSystem = new FileSystem(adapters); +``` \ No newline at end of file diff --git a/FileSystem.Adapters.Ftp/FileSystem.Adapters.Ftp.csproj b/FileSystem.Adapters.Ftp/FileSystem.Adapters.Ftp.csproj index 6c8c872..e542e02 100644 --- a/FileSystem.Adapters.Ftp/FileSystem.Adapters.Ftp.csproj +++ b/FileSystem.Adapters.Ftp/FileSystem.Adapters.Ftp.csproj @@ -13,7 +13,7 @@ - + diff --git a/FileSystem.Adapters.Ftp/README.md b/FileSystem.Adapters.Ftp/README.md new file mode 100644 index 0000000..b1efb26 --- /dev/null +++ b/FileSystem.Adapters.Ftp/README.md @@ -0,0 +1,20 @@ +# SharpGrip FileSystem Ftp adapter [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem.Adapters.Ftp)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.Ftp) + +## Installation + +Reference NuGet package `SharpGrip.FileSystem.Adapters.Ftp` (https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.Ftp). + +## Usage + +``` +// FTP connection. +var ftpClient = new AsyncFtpClient("hostname", "username", "password"); + +var adapters = new List +{ + new LocalAdapter("local", "/var/files"), + new FtpAdapter("ftp", "/var/files", ftpClient) +}; + +var fileSystem = new FileSystem(adapters); +``` \ No newline at end of file diff --git a/FileSystem.Adapters.GoogleCloudStorage/FileSystem.Adapters.GoogleCloudStorage.csproj b/FileSystem.Adapters.GoogleCloudStorage/FileSystem.Adapters.GoogleCloudStorage.csproj index e36621b..467853c 100644 --- a/FileSystem.Adapters.GoogleCloudStorage/FileSystem.Adapters.GoogleCloudStorage.csproj +++ b/FileSystem.Adapters.GoogleCloudStorage/FileSystem.Adapters.GoogleCloudStorage.csproj @@ -13,7 +13,7 @@ - + diff --git a/FileSystem.Adapters.GoogleCloudStorage/README.md b/FileSystem.Adapters.GoogleCloudStorage/README.md new file mode 100644 index 0000000..9d6e722 --- /dev/null +++ b/FileSystem.Adapters.GoogleCloudStorage/README.md @@ -0,0 +1,21 @@ +# SharpGrip FileSystem GoogleCloudStorage adapter [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem.Adapters.GoogleCloudStorage)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.GoogleCloudStorage) + +## Installation + +Reference NuGet package `SharpGrip.FileSystem.Adapters.GoogleCloudStorage` (https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.GoogleCloudStorage). + +## Usage + +``` +// Google connection. +var credential = GoogleCredential.FromFile("path/to/credential/file"); +var storageClient = await StorageClient.CreateAsync(credential); + +var adapters = new List +{ + new LocalAdapter("local", "/var/files"), + new GoogleCloudStorageAdapter(prefix, rootPath, storageClient, "bucketName", configuration); +}; + +var fileSystem = new FileSystem(adapters); +``` \ No newline at end of file diff --git a/FileSystem.Adapters.GoogleDrive/FileSystem.Adapters.GoogleDrive.csproj b/FileSystem.Adapters.GoogleDrive/FileSystem.Adapters.GoogleDrive.csproj index 10a6000..1edf365 100644 --- a/FileSystem.Adapters.GoogleDrive/FileSystem.Adapters.GoogleDrive.csproj +++ b/FileSystem.Adapters.GoogleDrive/FileSystem.Adapters.GoogleDrive.csproj @@ -13,7 +13,7 @@ - + diff --git a/FileSystem.Adapters.GoogleDrive/README.md b/FileSystem.Adapters.GoogleDrive/README.md new file mode 100644 index 0000000..054cd83 --- /dev/null +++ b/FileSystem.Adapters.GoogleDrive/README.md @@ -0,0 +1,33 @@ +# SharpGrip FileSystem GoogleDrive adapter [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem.Adapters.GoogleDrive)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.GoogleDrive) + +## Installation + +Reference NuGet package `SharpGrip.FileSystem.Adapters.GoogleDrive` (https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.GoogleDrive). + +## Usage + +``` +// Google connection. +await using var stream = new FileStream("path/to/credentials.json", FileMode.Open, FileAccess.Read); +const string tokenPath = "path/to/token/directory"; +var credential = await GoogleWebAuthorizationBroker.AuthorizeAsync( + (await GoogleClientSecrets.FromStreamAsync(stream)).Secrets, + new[] {DriveService.Scope.Drive}, + "user", + CancellationToken.None, + new FileDataStore(tokenPath, true)); + +var googleDriveClient = new DriveService(new BaseClientService.Initializer +{ + HttpClientInitializer = credential, + ApplicationName = "Test" +}); + +var adapters = new List +{ + new LocalAdapter("local", "/var/files"), + new GoogleDriveAdapter("google-drive", "/Files", googleDriveClient) +}; + +var fileSystem = new FileSystem(adapters); +``` \ No newline at end of file diff --git a/FileSystem.Adapters.MicrosoftOneDrive/FileSystem.Adapters.MicrosoftOneDrive.csproj b/FileSystem.Adapters.MicrosoftOneDrive/FileSystem.Adapters.MicrosoftOneDrive.csproj index 442c9e5..59c45dc 100644 --- a/FileSystem.Adapters.MicrosoftOneDrive/FileSystem.Adapters.MicrosoftOneDrive.csproj +++ b/FileSystem.Adapters.MicrosoftOneDrive/FileSystem.Adapters.MicrosoftOneDrive.csproj @@ -13,7 +13,7 @@ - + diff --git a/FileSystem.Adapters.MicrosoftOneDrive/README.md b/FileSystem.Adapters.MicrosoftOneDrive/README.md new file mode 100644 index 0000000..8e803c7 --- /dev/null +++ b/FileSystem.Adapters.MicrosoftOneDrive/README.md @@ -0,0 +1,32 @@ +# SharpGrip FileSystem MicrosoftOneDrive adapter [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem.Adapters.MicrosoftOneDrive)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.MicrosoftOneDrive) + +## Installation + +Reference NuGet package `SharpGrip.FileSystem.Adapters.MicrosoftOneDrive` (https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.MicrosoftOneDrive). + +## Usage + +``` +// Microsoft connection. +var scopes = new[] {"https://graph.microsoft.com/.default"}; +var tenantId = "tenantId"; +var confidentialClient = ConfidentialClientApplicationBuilder + .Create("clientId") + .WithAuthority($"https://login.microsoftonline.com/{tenantId}/v2.0") + .WithClientSecret("clientSecret") + .Build(); +var oneDriveClient = new GraphServiceClient(new DelegateAuthenticationProvider(async requestMessage => + { + var authResult = await confidentialClient.AcquireTokenForClient(scopes).ExecuteAsync(); + requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authResult.AccessToken); + }) +); + +var adapters = new List +{ + new LocalAdapter("local", "/var/files"), + new MicrosoftOneDriveAdapter("onedrive", "/Files", oneDriveClient, "driveId") +}; + +var fileSystem = new FileSystem(adapters); +``` \ No newline at end of file diff --git a/FileSystem.Adapters.Sftp/FileSystem.Adapters.Sftp.csproj b/FileSystem.Adapters.Sftp/FileSystem.Adapters.Sftp.csproj index 6fd1441..8fdf213 100644 --- a/FileSystem.Adapters.Sftp/FileSystem.Adapters.Sftp.csproj +++ b/FileSystem.Adapters.Sftp/FileSystem.Adapters.Sftp.csproj @@ -13,7 +13,7 @@ - + diff --git a/FileSystem.Adapters.Sftp/README.md b/FileSystem.Adapters.Sftp/README.md new file mode 100644 index 0000000..bed4ff0 --- /dev/null +++ b/FileSystem.Adapters.Sftp/README.md @@ -0,0 +1,23 @@ +# SharpGrip FileSystem Sftp adapter [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem.Adapters.Sftp)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.Sftp) + +## Installation + +Reference NuGet package `SharpGrip.FileSystem.Adapters.Sftp` (https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.Sftp). + +## Usage + +``` +// SFTP connection. +var privateKeyFile = new PrivateKeyFile("/home/userName/.ssh/id_rsa"); +var privateKeyAuthenticationMethod = new PrivateKeyAuthenticationMethod("userName", privateKeyFile); +var sftpConnectionInfo = new ConnectionInfo("hostName", "userName", privateKeyAuthenticationMethod); +var sftpClient = new SftpClient(sftpConnectionInfo); + +var adapters = new List +{ + new LocalAdapter("local", "/var/files"), + new SftpAdapter("sftp", "/var/files", sftpClient) +}; + +var fileSystem = new FileSystem(adapters); +``` \ No newline at end of file diff --git a/README.md b/README.md index 656a5d2..d34b409 100644 --- a/README.md +++ b/README.md @@ -12,9 +12,9 @@ ## Introduction -SharpGrip FileSystem is a versatile .NET file system abstraction that supports multiple storage adapters. -It empowers developers to manage various file systems and services through a unified and easily comprehensible API. -By coding against the abstractions provided by this library, developers can sidestep vendor-specific APIs, effectively avoiding vendor lock-ins. +SharpGrip FileSystem is a versatile .NET file system abstraction that supports multiple storage adapters. +It empowers developers to manage various file systems and services through a unified and easily comprehensible API. +By coding against the abstractions provided by this library, developers can sidestep vendor-specific APIs, effectively avoiding vendor lock-ins. This flexibility enhances the portability and maintainability of the codebase, allowing for smoother transitions between different file systems. ## Installation @@ -25,18 +25,18 @@ For adapters other than the local file system (included in the `SharpGrip.FileSy ## Supported adapters -| Adapter | Package | NuGet | -|:--------------------------------------------------|:---------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| [Local adapter](#local-adapter) | `SharpGrip.FileSystem` | [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem)](https://www.nuget.org/packages/SharpGrip.FileSystem) [![NuGet](https://img.shields.io/nuget/dt/SharpGrip.FileSystem)](https://www.nuget.org/packages/SharpGrip.FileSystem) | -| [AmazonS3](#amazons3-adapter) | `SharpGrip.FileSystem.Adapters.AmazonS3` | [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem.Adapters.AmazonS3)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.AmazonS3) [![NuGet](https://img.shields.io/nuget/dt/SharpGrip.FileSystem.Adapters.AmazonS3)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.AmazonS3) | -| [AzureBlobStorage](#azureblobstorage-adapter) | `SharpGrip.FileSystem.Adapters.AzureBlobStorage` | [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem.Adapters.AzureBlobStorage)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.AzureBlobStorage) [![NuGet](https://img.shields.io/nuget/dt/SharpGrip.FileSystem.Adapters.AzureBlobStorage)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.AzureBlobStorage) | -| [AzureFileStorage](#azurefilestorage-adapter) | `SharpGrip.FileSystem.Adapters.AzureFileStorage` | [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem.Adapters.AzureFileStorage)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.AzureFileStorage) [![NuGet](https://img.shields.io/nuget/dt/SharpGrip.FileSystem.Adapters.AzureFileStorage)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.AzureFileStorage) | -| [Dropbox](#dropbox-adapter) | `SharpGrip.FileSystem.Adapters.Dropbox` | [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem.Adapters.Dropbox)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.Dropbox) [![NuGet](https://img.shields.io/nuget/dt/SharpGrip.FileSystem.Adapters.Dropbox)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.Dropbox) | -| [FTP](#ftp-adapter) | `SharpGrip.FileSystem.Adapters.Ftp` | [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem.Adapters.Ftp)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.Ftp) [![NuGet](https://img.shields.io/nuget/dt/SharpGrip.FileSystem.Adapters.Ftp)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.Ftp) | -| [GoogleCloudStorage](#googlecloudstorage-adapter) | `SharpGrip.FileSystem.Adapters.GoogleCloudStorage` | [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem.Adapters.GoogleCloudStorage)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.GoogleCloudStorage) [![NuGet](https://img.shields.io/nuget/dt/SharpGrip.FileSystem.Adapters.GoogleCloudStorage)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.GoogleCloudStorage) | -| [GoogleDrive](#googledrive-adapter) | `SharpGrip.FileSystem.Adapters.GoogleDrive` | [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem.Adapters.GoogleDrive)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.GoogleDrive) [![NuGet](https://img.shields.io/nuget/dt/SharpGrip.FileSystem.Adapters.GoogleDrive)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.GoogleDrive) | -| [MicrosoftOneDrive](#microsoftonedrive-adapter) | `SharpGrip.FileSystem.Adapters.MicrosoftOneDrive` | [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem.Adapters.MicrosoftOneDrive)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.MicrosoftOneDrive) [![NuGet](https://img.shields.io/nuget/dt/SharpGrip.FileSystem.Adapters.MicrosoftOneDrive)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.MicrosoftOneDrive) | -| [SFTP](#sftp-adapter) | `SharpGrip.FileSystem.Adapters.Sftp` | [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem.Adapters.Sftp)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.Sftp) [![NuGet](https://img.shields.io/nuget/dt/SharpGrip.FileSystem.Adapters.Sftp)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.Sftp) | +| Adapter | Package | NuGet | +|:-----------------------------------------------------------------------|:---------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| [Local adapter](#local-adapter) | `SharpGrip.FileSystem` | [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem)](https://www.nuget.org/packages/SharpGrip.FileSystem) [![NuGet](https://img.shields.io/nuget/dt/SharpGrip.FileSystem)](https://www.nuget.org/packages/SharpGrip.FileSystem) | +| [AmazonS3](FileSystem.Adapters.AmazonS3/README.md) | `SharpGrip.FileSystem.Adapters.AmazonS3` | [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem.Adapters.AmazonS3)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.AmazonS3) [![NuGet](https://img.shields.io/nuget/dt/SharpGrip.FileSystem.Adapters.AmazonS3)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.AmazonS3) | +| [AzureBlobStorage](FileSystem.Adapters.AzureBlobStorage/README.md) | `SharpGrip.FileSystem.Adapters.AzureBlobStorage` | [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem.Adapters.AzureBlobStorage)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.AzureBlobStorage) [![NuGet](https://img.shields.io/nuget/dt/SharpGrip.FileSystem.Adapters.AzureBlobStorage)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.AzureBlobStorage) | +| [AzureFileStorage](FileSystem.Adapters.AzureFileStorage/README.md) | `SharpGrip.FileSystem.Adapters.AzureFileStorage` | [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem.Adapters.AzureFileStorage)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.AzureFileStorage) [![NuGet](https://img.shields.io/nuget/dt/SharpGrip.FileSystem.Adapters.AzureFileStorage)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.AzureFileStorage) | +| [Dropbox](FileSystem.Adapters.Dropbox/README.md) | `SharpGrip.FileSystem.Adapters.Dropbox` | [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem.Adapters.Dropbox)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.Dropbox) [![NuGet](https://img.shields.io/nuget/dt/SharpGrip.FileSystem.Adapters.Dropbox)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.Dropbox) | +| [FTP](FileSystem.Adapters.Ftp/README.md) | `SharpGrip.FileSystem.Adapters.Ftp` | [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem.Adapters.Ftp)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.Ftp) [![NuGet](https://img.shields.io/nuget/dt/SharpGrip.FileSystem.Adapters.Ftp)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.Ftp) | +| [GoogleCloudStorage](FileSystem.Adapters.GoogleCloudStorage/README.md) | `SharpGrip.FileSystem.Adapters.GoogleCloudStorage` | [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem.Adapters.GoogleCloudStorage)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.GoogleCloudStorage) [![NuGet](https://img.shields.io/nuget/dt/SharpGrip.FileSystem.Adapters.GoogleCloudStorage)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.GoogleCloudStorage) | +| [GoogleDrive](FileSystem.Adapters.GoogleDrive/README.md) | `SharpGrip.FileSystem.Adapters.GoogleDrive` | [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem.Adapters.GoogleDrive)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.GoogleDrive) [![NuGet](https://img.shields.io/nuget/dt/SharpGrip.FileSystem.Adapters.GoogleDrive)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.GoogleDrive) | +| [MicrosoftOneDrive](FileSystem.Adapters.MicrosoftOneDrive/README.md) | `SharpGrip.FileSystem.Adapters.MicrosoftOneDrive` | [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem.Adapters.MicrosoftOneDrive)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.MicrosoftOneDrive) [![NuGet](https://img.shields.io/nuget/dt/SharpGrip.FileSystem.Adapters.MicrosoftOneDrive)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.MicrosoftOneDrive) | +| [SFTP](FileSystem.Adapters.Sftp/README.md) | `SharpGrip.FileSystem.Adapters.Sftp` | [![NuGet](https://img.shields.io/nuget/v/SharpGrip.FileSystem.Adapters.Sftp)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.Sftp) [![NuGet](https://img.shields.io/nuget/dt/SharpGrip.FileSystem.Adapters.Sftp)](https://www.nuget.org/packages/SharpGrip.FileSystem.Adapters.Sftp) | ## Supported operations @@ -72,171 +72,6 @@ var adapters = new List var fileSystem = new FileSystem(adapters); ``` -### AmazonS3 adapter - -``` -// Amazon connection. -var amazonClient = new AmazonS3Client("awsAccessKeyId", "awsSecretAccessKey", RegionEndpoint.USEast2); - -var adapters = new List -{ - new LocalAdapter("local", "/var/files"), - new AmazonS3Adapter("amazon", "/Files", amazonClient, "bucketName") -}; - -var fileSystem = new FileSystem(adapters); -``` - -### AzureBlobStorage adapter - -``` -// Azure connection. -var blobServiceClient = new BlobServiceClient("connectionString"); -var azureClient = blobServiceClient.GetBlobContainerClient("blobContainerName"); - -var adapters = new List -{ - new LocalAdapter("local", "/var/files"), - new AzureBlobStorageAdapter("azure", "/Files", azureClient) -}; - -var fileSystem = new FileSystem(adapters); -``` - -### AzureFileStorage adapter - -``` -// Azure connection. -var azureClient = new ShareClient("connectionString", "shareName"); - -var adapters = new List -{ - new LocalAdapter("local", "/var/files"), - new AzureFileStorageAdapter("azure", "/Files", azureClient) -}; - -var fileSystem = new FileSystem(adapters); -``` - -### Dropbox adapter - -``` -// Dropbox connection. -var dropboxClient = new DropboxClient("oAuth2AccessToken"); - -var adapters = new List -{ - new LocalAdapter("local", "/var/files"), - new DropboxAdapter("dropbox", "/Files", dropboxClient) -}; - -var fileSystem = new FileSystem(adapters); -``` - -### FTP adapter - -``` -// FTP connection. -var ftpClient = new AsyncFtpClient("hostname", "username", "password"); - -var adapters = new List -{ - new LocalAdapter("local", "/var/files"), - new FtpAdapter("ftp", "/var/files", ftpClient) -}; - -var fileSystem = new FileSystem(adapters); -``` - -### GoogleCloudStorage adapter - -``` -// Google connection. -var credential = GoogleCredential.FromFile("path/to/credential/file"); -var storageClient = await StorageClient.CreateAsync(credential); - -var adapters = new List -{ - new LocalAdapter("local", "/var/files"), - new GoogleCloudStorageAdapter(prefix, rootPath, storageClient, "bucketName", configuration); -}; - -var fileSystem = new FileSystem(adapters); -``` - -### GoogleDrive adapter - -``` -// Google connection. -await using var stream = new FileStream("path/to/credentials.json", FileMode.Open, FileAccess.Read); -const string tokenPath = "path/to/token/directory"; -var credential = await GoogleWebAuthorizationBroker.AuthorizeAsync( - (await GoogleClientSecrets.FromStreamAsync(stream)).Secrets, - new[] {DriveService.Scope.Drive}, - "user", - CancellationToken.None, - new FileDataStore(tokenPath, true)); - -var googleDriveClient = new DriveService(new BaseClientService.Initializer -{ - HttpClientInitializer = credential, - ApplicationName = "Test" -}); - -var adapters = new List -{ - new LocalAdapter("local", "/var/files"), - new GoogleDriveAdapter("google-drive", "/Files", googleDriveClient) -}; - -var fileSystem = new FileSystem(adapters); -``` - -### MicrosoftOneDrive adapter - -``` -// Microsoft connection. -var scopes = new[] {"https://graph.microsoft.com/.default"}; -var tenantId = "tenantId"; -var confidentialClient = ConfidentialClientApplicationBuilder - .Create("clientId") - .WithAuthority($"https://login.microsoftonline.com/{tenantId}/v2.0") - .WithClientSecret("clientSecret") - .Build(); -var oneDriveClient = new GraphServiceClient(new DelegateAuthenticationProvider(async requestMessage => - { - var authResult = await confidentialClient.AcquireTokenForClient(scopes).ExecuteAsync(); - requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authResult.AccessToken); - }) -); - -var adapters = new List -{ - new LocalAdapter("local", "/var/files"), - new MicrosoftOneDriveAdapter("onedrive", "/Files", oneDriveClient, "driveId") -}; - -var fileSystem = new FileSystem(adapters); -``` - -### SFTP adapter - -``` -// SFTP connection. -var privateKeyFile = new PrivateKeyFile("/home/userName/.ssh/id_rsa"); -var privateKeyAuthenticationMethod = new PrivateKeyAuthenticationMethod("userName", privateKeyFile); -var sftpConnectionInfo = new ConnectionInfo("hostName", "userName", privateKeyAuthenticationMethod); -var sftpClient = new SftpClient(sftpConnectionInfo); - -var adapters = new List -{ - new LocalAdapter("local", "/var/files"), - new SftpAdapter("sftp", "/var/files", sftpClient) -}; - -var fileSystem = new FileSystem(adapters); -``` - ### Example operations ``` From 06bbce1fc6242617490eaba730411b71679d1a28 Mon Sep 17 00:00:00 2001 From: Mauro van der Gun Date: Wed, 22 May 2024 18:56:26 -0400 Subject: [PATCH 6/8] delay disposing of stream to after writing --- FileSystem/src/FileSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FileSystem/src/FileSystem.cs b/FileSystem/src/FileSystem.cs index 28d62ce..72c4336 100644 --- a/FileSystem/src/FileSystem.cs +++ b/FileSystem/src/FileSystem.cs @@ -954,7 +954,7 @@ public async Task MoveFileAsync(string virtualSourcePath, string virtualDestinat destinationAdapter.ClearCache(); } - using var fileStream = await sourceAdapter.ReadFileStreamAsync(virtualSourcePath, cancellationToken); + var fileStream = await sourceAdapter.ReadFileStreamAsync(virtualSourcePath, cancellationToken); await destinationAdapter.WriteFileAsync(virtualDestinationPath, fileStream, overwrite, cancellationToken); fileStream.Dispose(); From 99d6a98e977c53674946dafada390a2e5c24bd4b Mon Sep 17 00:00:00 2001 From: Mauro van der Gun Date: Wed, 22 May 2024 20:41:00 -0400 Subject: [PATCH 7/8] package updates --- .../FileSystem.Adapters.AmazonS3.csproj | 2 +- .../FileSystem.Adapters.AzureBlobStorage.csproj | 2 +- .../FileSystem.Adapters.AzureFileStorage.csproj | 2 +- .../FileSystem.Adapters.Dropbox.csproj | 2 +- .../FileSystem.Adapters.Ftp.csproj | 2 +- .../FileSystem.Adapters.GoogleCloudStorage.csproj | 2 +- .../FileSystem.Adapters.GoogleDrive.csproj | 2 +- .../FileSystem.Adapters.MicrosoftOneDrive.csproj | 2 +- .../FileSystem.Adapters.Sftp.csproj | 2 +- Tests/Tests.csproj | 14 +++++++------- 10 files changed, 16 insertions(+), 16 deletions(-) diff --git a/FileSystem.Adapters.AmazonS3/FileSystem.Adapters.AmazonS3.csproj b/FileSystem.Adapters.AmazonS3/FileSystem.Adapters.AmazonS3.csproj index 6e8d34d..42901eb 100644 --- a/FileSystem.Adapters.AmazonS3/FileSystem.Adapters.AmazonS3.csproj +++ b/FileSystem.Adapters.AmazonS3/FileSystem.Adapters.AmazonS3.csproj @@ -17,7 +17,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/FileSystem.Adapters.AzureBlobStorage/FileSystem.Adapters.AzureBlobStorage.csproj b/FileSystem.Adapters.AzureBlobStorage/FileSystem.Adapters.AzureBlobStorage.csproj index 9db2cc1..ced6707 100644 --- a/FileSystem.Adapters.AzureBlobStorage/FileSystem.Adapters.AzureBlobStorage.csproj +++ b/FileSystem.Adapters.AzureBlobStorage/FileSystem.Adapters.AzureBlobStorage.csproj @@ -17,7 +17,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/FileSystem.Adapters.AzureFileStorage/FileSystem.Adapters.AzureFileStorage.csproj b/FileSystem.Adapters.AzureFileStorage/FileSystem.Adapters.AzureFileStorage.csproj index bc11517..3d374d6 100644 --- a/FileSystem.Adapters.AzureFileStorage/FileSystem.Adapters.AzureFileStorage.csproj +++ b/FileSystem.Adapters.AzureFileStorage/FileSystem.Adapters.AzureFileStorage.csproj @@ -17,7 +17,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/FileSystem.Adapters.Dropbox/FileSystem.Adapters.Dropbox.csproj b/FileSystem.Adapters.Dropbox/FileSystem.Adapters.Dropbox.csproj index da51d91..bb9f54e 100644 --- a/FileSystem.Adapters.Dropbox/FileSystem.Adapters.Dropbox.csproj +++ b/FileSystem.Adapters.Dropbox/FileSystem.Adapters.Dropbox.csproj @@ -17,7 +17,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/FileSystem.Adapters.Ftp/FileSystem.Adapters.Ftp.csproj b/FileSystem.Adapters.Ftp/FileSystem.Adapters.Ftp.csproj index e542e02..83ede9d 100644 --- a/FileSystem.Adapters.Ftp/FileSystem.Adapters.Ftp.csproj +++ b/FileSystem.Adapters.Ftp/FileSystem.Adapters.Ftp.csproj @@ -17,7 +17,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/FileSystem.Adapters.GoogleCloudStorage/FileSystem.Adapters.GoogleCloudStorage.csproj b/FileSystem.Adapters.GoogleCloudStorage/FileSystem.Adapters.GoogleCloudStorage.csproj index 467853c..e6bef0d 100644 --- a/FileSystem.Adapters.GoogleCloudStorage/FileSystem.Adapters.GoogleCloudStorage.csproj +++ b/FileSystem.Adapters.GoogleCloudStorage/FileSystem.Adapters.GoogleCloudStorage.csproj @@ -17,7 +17,7 @@ - + diff --git a/FileSystem.Adapters.GoogleDrive/FileSystem.Adapters.GoogleDrive.csproj b/FileSystem.Adapters.GoogleDrive/FileSystem.Adapters.GoogleDrive.csproj index 1edf365..e75cb58 100644 --- a/FileSystem.Adapters.GoogleDrive/FileSystem.Adapters.GoogleDrive.csproj +++ b/FileSystem.Adapters.GoogleDrive/FileSystem.Adapters.GoogleDrive.csproj @@ -17,7 +17,7 @@ - + diff --git a/FileSystem.Adapters.MicrosoftOneDrive/FileSystem.Adapters.MicrosoftOneDrive.csproj b/FileSystem.Adapters.MicrosoftOneDrive/FileSystem.Adapters.MicrosoftOneDrive.csproj index 59c45dc..422edf7 100644 --- a/FileSystem.Adapters.MicrosoftOneDrive/FileSystem.Adapters.MicrosoftOneDrive.csproj +++ b/FileSystem.Adapters.MicrosoftOneDrive/FileSystem.Adapters.MicrosoftOneDrive.csproj @@ -18,7 +18,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/FileSystem.Adapters.Sftp/FileSystem.Adapters.Sftp.csproj b/FileSystem.Adapters.Sftp/FileSystem.Adapters.Sftp.csproj index 8fdf213..ac19ad0 100644 --- a/FileSystem.Adapters.Sftp/FileSystem.Adapters.Sftp.csproj +++ b/FileSystem.Adapters.Sftp/FileSystem.Adapters.Sftp.csproj @@ -21,7 +21,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj index 3f7b907..4c1f556 100644 --- a/Tests/Tests.csproj +++ b/Tests/Tests.csproj @@ -8,26 +8,26 @@ - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive From 64324e5f82daff906569d329ede29ae6f573c341 Mon Sep 17 00:00:00 2001 From: Mauro van der Gun Date: Wed, 22 May 2024 20:48:49 -0400 Subject: [PATCH 8/8] update actions --- .github/workflows/Build.yaml | 6 +++--- .github/workflows/Release.yaml | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/Build.yaml b/.github/workflows/Build.yaml index 0cfcc93..06231f8 100644 --- a/.github/workflows/Build.yaml +++ b/.github/workflows/Build.yaml @@ -28,18 +28,18 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Set up Java - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: ${{ env.JAVA_VERSION }} distribution: ${{ env.JAVA_DISTRIBUTION }} - name: Set up .NET - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: dotnet-version: ${{ env.DOTNET_VERSION }} diff --git a/.github/workflows/Release.yaml b/.github/workflows/Release.yaml index 2bc0023..8fd76c4 100644 --- a/.github/workflows/Release.yaml +++ b/.github/workflows/Release.yaml @@ -18,12 +18,12 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Set up .NET - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: dotnet-version: ${{ env.DOTNET_VERSION }}