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 }} diff --git a/FileSystem.Adapters.AmazonS3/FileSystem.Adapters.AmazonS3.csproj b/FileSystem.Adapters.AmazonS3/FileSystem.Adapters.AmazonS3.csproj index d5056a1..42901eb 100644 --- a/FileSystem.Adapters.AmazonS3/FileSystem.Adapters.AmazonS3.csproj +++ b/FileSystem.Adapters.AmazonS3/FileSystem.Adapters.AmazonS3.csproj @@ -13,11 +13,11 @@ - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive 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.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/FileSystem.Adapters.AzureBlobStorage.csproj b/FileSystem.Adapters.AzureBlobStorage/FileSystem.Adapters.AzureBlobStorage.csproj index ca2ba34..ced6707 100644 --- a/FileSystem.Adapters.AzureBlobStorage/FileSystem.Adapters.AzureBlobStorage.csproj +++ b/FileSystem.Adapters.AzureBlobStorage/FileSystem.Adapters.AzureBlobStorage.csproj @@ -13,11 +13,11 @@ - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive 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.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/FileSystem.Adapters.AzureFileStorage.csproj b/FileSystem.Adapters.AzureFileStorage/FileSystem.Adapters.AzureFileStorage.csproj index 94c929f..3d374d6 100644 --- a/FileSystem.Adapters.AzureFileStorage/FileSystem.Adapters.AzureFileStorage.csproj +++ b/FileSystem.Adapters.AzureFileStorage/FileSystem.Adapters.AzureFileStorage.csproj @@ -13,11 +13,11 @@ - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive 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.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/FileSystem.Adapters.Dropbox.csproj b/FileSystem.Adapters.Dropbox/FileSystem.Adapters.Dropbox.csproj index cec4561..bb9f54e 100644 --- a/FileSystem.Adapters.Dropbox/FileSystem.Adapters.Dropbox.csproj +++ b/FileSystem.Adapters.Dropbox/FileSystem.Adapters.Dropbox.csproj @@ -13,11 +13,11 @@ - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive 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.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..83ede9d --- /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/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.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.GoogleCloudStorage/FileSystem.Adapters.GoogleCloudStorage.csproj b/FileSystem.Adapters.GoogleCloudStorage/FileSystem.Adapters.GoogleCloudStorage.csproj new file mode 100644 index 0000000..e6bef0d --- /dev/null +++ b/FileSystem.Adapters.GoogleCloudStorage/FileSystem.Adapters.GoogleCloudStorage.csproj @@ -0,0 +1,27 @@ + + + + SharpGrip.FileSystem.Adapters.GoogleCloudStorage + + + + SharpGrip.FileSystem.Adapters.GoogleCloudStorage + SharpGrip.FileSystem.Adapters.GoogleCloudStorage + SharpGrip FileSystem Google Cloud Storage adapter + The SharpGrip FileSystem Google Cloud Storage adapter. + sharpgrip;file-system;google-cloud-storage + + + + + + + + + + + + + + + \ No newline at end of file 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.GoogleCloudStorage/src/GoogleCloudStorageAdapter.cs b/FileSystem.Adapters.GoogleCloudStorage/src/GoogleCloudStorageAdapter.cs new file mode 100644 index 0000000..f8f2d62 --- /dev/null +++ b/FileSystem.Adapters.GoogleCloudStorage/src/GoogleCloudStorageAdapter.cs @@ -0,0 +1,321 @@ +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 +{ + public class GoogleCloudStorageAdapter : Adapter + { + private readonly StorageClient client; + private readonly string bucketName; + + public GoogleCloudStorageAdapter(string prefix, string rootPath, StorageClient client, string bucketName, Action? configuration = null) + : base(prefix, rootPath, configuration) + { + this.client = client; + this.bucketName = bucketName; + } + + public override void Dispose() + { + client.Dispose(); + } + + public override async Task ConnectAsync(CancellationToken cancellationToken = default) + { + Logger.LogStartConnectingAdapter(this); + await Task.CompletedTask; + Logger.LogFinishedConnectingAdapter(this); + } + + public override async Task GetFileAsync(string virtualPath, CancellationToken cancellationToken = default) + { + var path = GetPath(virtualPath).RemoveLeadingForwardSlash().RemoveTrailingForwardSlash(); + + try + { + var file = await client.GetObjectAsync(bucketName, path, new GetObjectOptions(), cancellationToken); + + if (file == null) + { + throw new FileNotFoundException(path, Prefix); + } + + 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); + } + } + + public override async Task GetDirectoryAsync(string virtualPath, CancellationToken cancellationToken = default) + { + 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; + request.Delimiter = "/"; + + do + { + var objects = await request.ExecuteAsync(cancellationToken: cancellationToken); + + if (objects.Prefixes != null) + { + foreach (var directoryPath in objects.Prefixes) + { + if (directoryPath == path) + { + var directoryName = GetLastPathPart(directoryPath); + + return ModelFactory.CreateDirectory(directoryName.RemoveTrailingForwardSlash(), directoryPath.EnsureTrailingForwardSlash(), GetVirtualPath(directoryPath)); + } + } + } + + request.PageToken = objects.NextPageToken; + } while (request.PageToken != null); + + throw new DirectoryNotFoundException(path, Prefix); + } + catch (GoogleApiException googleApiException) when (googleApiException.HttpStatusCode == HttpStatusCode.NotFound) + { + throw new DirectoryNotFoundException(path, Prefix); + } + catch (Exception exception) + { + throw Exception(exception); + } + } + + public override async Task> GetFilesAsync(string virtualPath = "", CancellationToken cancellationToken = default) + { + await GetDirectoryAsync(virtualPath, cancellationToken); + + var path = GetPath(virtualPath).RemoveLeadingForwardSlash().EnsureTrailingForwardSlash(); + + try + { + var files = new List(); + + var request = client.Service.Objects.List(bucketName); + + request.Prefix = path == "/" ? null : path; + request.Delimiter = "/"; + + do + { + var objects = await request.ExecuteAsync(cancellationToken: cancellationToken); + + if (objects.Items != null) + { + 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; + } while (request.PageToken != null); + + return files; + } + catch (Exception exception) + { + throw Exception(exception); + } + } + + public override async Task> GetDirectoriesAsync(string virtualPath = "", CancellationToken cancellationToken = default) + { + await GetDirectoryAsync(virtualPath, cancellationToken); + + var path = GetPath(virtualPath).RemoveLeadingForwardSlash().EnsureTrailingForwardSlash(); + + try + { + var directories = new List(); + + var request = client.Service.Objects.List(bucketName); + + request.Prefix = path == "/" ? null : path; + request.Delimiter = "/"; + + do + { + var objects = await request.ExecuteAsync(cancellationToken: cancellationToken); + + if (objects.Prefixes != null) + { + foreach (var directoryPath in objects.Prefixes) + { + var directoryName = GetLastPathPart(directoryPath); + + directories.Add(ModelFactory.CreateDirectory(directoryName.RemoveTrailingForwardSlash(), directoryPath.EnsureTrailingForwardSlash(), GetVirtualPath(directoryPath))); + } + } + + request.PageToken = objects.NextPageToken; + } while (request.PageToken != null); + + return directories; + } + 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); + } + + var path = GetPath(virtualPath).RemoveLeadingForwardSlash().EnsureTrailingForwardSlash(); + + try + { + await client.UploadObjectAsync(bucketName, path.EnsureTrailingForwardSlash(), null, Stream.Null, cancellationToken: cancellationToken); + } + catch (Exception exception) + { + throw Exception(exception); + } + } + + public override async Task DeleteDirectoryAsync(string virtualPath, CancellationToken cancellationToken = default) + { + 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) + { + 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) + { + 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) + { + 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) + { + if (exception is FileSystemException) + { + return exception; + } + + return new AdapterRuntimeException(exception); + } + } +} \ No newline at end of file diff --git a/FileSystem.Adapters.GoogleCloudStorage/src/GoogleCloudStorageAdapterConfiguration.cs b/FileSystem.Adapters.GoogleCloudStorage/src/GoogleCloudStorageAdapterConfiguration.cs new file mode 100644 index 0000000..dc83161 --- /dev/null +++ b/FileSystem.Adapters.GoogleCloudStorage/src/GoogleCloudStorageAdapterConfiguration.cs @@ -0,0 +1,8 @@ +using SharpGrip.FileSystem.Configuration; + +namespace SharpGrip.FileSystem.Adapters.GoogleCloudStorage +{ + public class GoogleCloudStorageAdapterConfiguration : AdapterConfiguration + { + } +} \ No newline at end of file diff --git a/FileSystem.Adapters.GoogleCloudStorage/src/ModelFactory.cs b/FileSystem.Adapters.GoogleCloudStorage/src/ModelFactory.cs new file mode 100644 index 0000000..6a49dab --- /dev/null +++ b/FileSystem.Adapters.GoogleCloudStorage/src/ModelFactory.cs @@ -0,0 +1,44 @@ +using Google.Apis.Storage.v1.Data; +using SharpGrip.FileSystem.Models; +using SharpGrip.FileSystem.Utilities; + +namespace SharpGrip.FileSystem.Adapters.GoogleCloudStorage +{ + public static class ModelFactory + { + public static FileModel CreateFile(Object file, string path, string virtualPath) + { + return new FileModel + { + Name = PathUtilities.GetLastPathPart(file.Name), + Path = path, + VirtualPath = virtualPath, + Length = (long?) file.Size, + LastModifiedDateTime = file.UpdatedDateTimeOffset?.DateTime, + CreatedDateTime = file.TimeCreatedDateTimeOffset?.DateTime + }; + } + + public static DirectoryModel CreateDirectory(string name, string path, string virtualPath) + { + return new DirectoryModel + { + Name = name, + Path = path, + VirtualPath = virtualPath + }; + } + + public static DirectoryModel CreateDirectory(Object directory, string path, string virtualPath) + { + return new DirectoryModel + { + Name = directory.Name, + Path = path, + VirtualPath = virtualPath, + LastModifiedDateTime = directory.UpdatedDateTimeOffset?.DateTime, + CreatedDateTime = directory.TimeCreatedDateTimeOffset?.DateTime + }; + } + } +} \ 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..e75cb58 100644 --- a/FileSystem.Adapters.GoogleDrive/FileSystem.Adapters.GoogleDrive.csproj +++ b/FileSystem.Adapters.GoogleDrive/FileSystem.Adapters.GoogleDrive.csproj @@ -13,11 +13,11 @@ - + - + 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.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/FileSystem.Adapters.MicrosoftOneDrive.csproj b/FileSystem.Adapters.MicrosoftOneDrive/FileSystem.Adapters.MicrosoftOneDrive.csproj index 442c9e5..422edf7 100644 --- a/FileSystem.Adapters.MicrosoftOneDrive/FileSystem.Adapters.MicrosoftOneDrive.csproj +++ b/FileSystem.Adapters.MicrosoftOneDrive/FileSystem.Adapters.MicrosoftOneDrive.csproj @@ -13,12 +13,12 @@ - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive 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.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..ac19ad0 100644 --- a/FileSystem.Adapters.Sftp/FileSystem.Adapters.Sftp.csproj +++ b/FileSystem.Adapters.Sftp/FileSystem.Adapters.Sftp.csproj @@ -13,7 +13,7 @@ - + @@ -21,7 +21,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + 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/FileSystem.Adapters.Sftp/src/SftpAdapter.cs b/FileSystem.Adapters.Sftp/src/SftpAdapter.cs index 8c0aad4..071332a 100644 --- a/FileSystem.Adapters.Sftp/src/SftpAdapter.cs +++ b/FileSystem.Adapters.Sftp/src/SftpAdapter.cs @@ -32,10 +32,10 @@ public SftpAdapter(string prefix, string rootPath, ISftpClient client, ActionSharpGrip.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-cloud-storage;google-drive;microsoft-onedrive;sftp diff --git a/FileSystem/src/Adapters/Adapter.cs b/FileSystem/src/Adapters/Adapter.cs index aa39db7..265445f 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); @@ -128,9 +129,9 @@ public virtual async Task AppendFileAsync(string virtualPath, Stream contents, C public abstract Task WriteFileAsync(string virtualPath, Stream contents, bool overwrite = false, CancellationToken cancellationToken = default); protected abstract Exception Exception(Exception exception); - protected string GetPath(string path) + protected string GetPath(string virtualPath) { - return PathUtilities.GetPath(path, RootPath); + return PathUtilities.GetPath(virtualPath, RootPath); } protected string GetVirtualPath(string path) 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..72c4336 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,27 +931,30 @@ 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) { 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(); @@ -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 4e2d720..d34b409 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,16 +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) | -| [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 @@ -67,140 +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); -``` - -### 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 ``` 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 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]