Skip to content

Commit

Permalink
Merge pull request #12 from rameel/refactoring
Browse files Browse the repository at this point in the history
Refactoring and clean up
  • Loading branch information
rameel authored Sep 3, 2024
2 parents 2e1a777 + c5885bc commit 5acc1f0
Show file tree
Hide file tree
Showing 26 changed files with 594 additions and 353 deletions.
4 changes: 2 additions & 2 deletions src/Ramstack.FileSystem.Abstractions/Internal/ThrowHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ public static void ChangesNotSupported() =>
[DoesNotReturn]
public static void PathMappingFailed()
{
const string message = "The virtual path could not be mapped to a physical path. The parent directory may not exist or be accessible.";
throw new InvalidOperationException(message);
const string Message = "The virtual path could not be mapped to a physical path. The parent directory may not exist or be accessible.";
throw new InvalidOperationException(Message);
}

/// <summary>
Expand Down
54 changes: 54 additions & 0 deletions src/Ramstack.FileSystem.Abstractions/VirtualFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -196,4 +196,58 @@ protected virtual async ValueTask CopyCoreAsync(string destinationPath, bool ove
await using var source = await OpenReadAsync(cancellationToken).ConfigureAwait(false);
await FileSystem.WriteFileAsync(destinationPath, source, overwrite, cancellationToken).ConfigureAwait(false);
}

/// <summary>
/// Asynchronously copies the contents of the current <see cref="VirtualFile"/> to the specified destination <see cref="VirtualFile"/>.
/// </summary>
/// <param name="destination">The destination <see cref="VirtualFile"/> where the contents will be copied to.</param>
/// <param name="overwrite"><see langword="true"/> to overwrite an existing file; <see langword="false"/> to throw an exception if the file already exists.</param>
/// <param name="cancellationToken">An optional cancellation token to cancel the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that represents the asynchronous copy operation.
/// </returns>
/// <remarks>
/// <list type="bullet">
/// <item><description>If the file does not exist, it will be created.</description></item>
/// <item><description>If it exists and <paramref name="overwrite"/> is <see langword="true"/>, the existing file will be overwritten.</description></item>
/// <item><description>If <paramref name="overwrite"/> is <see langword="false"/> and the file exists, an exception will be thrown.</description></item>
/// </list>
/// </remarks>
public ValueTask CopyToAsync(VirtualFile destination, bool overwrite, CancellationToken cancellationToken = default)
{
if (destination.IsReadOnly)
ThrowHelper.ChangesNotSupported();

return CopyToCoreAsync(destination, overwrite, cancellationToken);
}

/// <summary>
/// Core implementation for asynchronously copying the contents of the current <see cref="VirtualFile"/> to the specified destination <see cref="VirtualFile"/>.
/// </summary>
/// <param name="destination">The destination <see cref="VirtualFile"/> where the contents will be copied to.</param>
/// <param name="overwrite"><see langword="true"/> to overwrite an existing file; <see langword="false"/> to throw an exception if the file already exists.</param>
/// <param name="cancellationToken">An optional cancellation token to cancel the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that represents the asynchronous copy operation.
/// </returns>
/// <remarks>
/// <list type="bullet">
/// <item><description>If the file does not exist, it will be created.</description></item>
/// <item><description>If it exists and <paramref name="overwrite"/> is <see langword="true"/>, the existing file will be overwritten.</description></item>
/// <item><description>If <paramref name="overwrite"/> is <see langword="false"/> and the file exists, an exception will be thrown.</description></item>
/// </list>
/// </remarks>
protected virtual async ValueTask CopyToCoreAsync(VirtualFile destination, bool overwrite, CancellationToken cancellationToken)
{
if (FileSystem == destination.FileSystem)
{
await CopyAsync(destination.FullName, overwrite, cancellationToken).ConfigureAwait(false);
destination.Refresh();
}
else
{
await using var stream = await OpenReadAsync(cancellationToken).ConfigureAwait(false);
await destination.WriteAsync(stream, overwrite, cancellationToken).ConfigureAwait(false);
}
}
}
31 changes: 0 additions & 31 deletions src/Ramstack.FileSystem.Abstractions/VirtualFileExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,37 +61,6 @@ public static ValueTask WriteAsync(this VirtualFile file, Stream stream, Cancell
public static ValueTask CopyToAsync(this VirtualFile file, VirtualFile destination, CancellationToken cancellationToken = default) =>
file.CopyToAsync(destination, overwrite: false, cancellationToken);

/// <summary>
/// Asynchronously copies the contents of the current <see cref="VirtualFile"/> to the specified destination <see cref="VirtualFile"/>.
/// </summary>
/// <param name="file">The source <see cref="VirtualFile"/> to copy from.</param>
/// <param name="destination">The destination <see cref="VirtualFile"/> where the contents will be copied to.</param>
/// <param name="overwrite"><see langword="true"/> to overwrite an existing file; <see langword="false"/> to throw an exception if the file already exists.</param>
/// <param name="cancellationToken">A token to cancel the operation. Defaults to <see cref="CancellationToken.None"/>.</param>
/// <returns>
/// A <see cref="ValueTask"/> that represents the asynchronous copy operation.
/// </returns>
/// <remarks>
/// <list type="bullet">
/// <item><description>If the file does not exist, it will be created.</description></item>
/// <item><description>If it exists and <paramref name="overwrite"/> is <see langword="true"/>, the existing file will be overwritten.</description></item>
/// <item><description>If <paramref name="overwrite"/> is <see langword="false"/> and the file exists, an exception will be thrown.</description></item>
/// </list>
/// </remarks>
public static async ValueTask CopyToAsync(this VirtualFile file, VirtualFile destination, bool overwrite, CancellationToken cancellationToken = default)
{
if (file.FileSystem == destination.FileSystem)
{
await file.CopyAsync(destination.FullName, overwrite, cancellationToken).ConfigureAwait(false);
destination.Refresh();
}
else
{
await using var stream = await file.OpenReadAsync(cancellationToken).ConfigureAwait(false);
await destination.WriteAsync(stream, overwrite, cancellationToken).ConfigureAwait(false);
}
}

/// <summary>
/// Asynchronously returns the size of the specified file in bytes.
/// </summary>
Expand Down
66 changes: 66 additions & 0 deletions src/Ramstack.FileSystem.Amazon/AccessControl.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
namespace Ramstack.FileSystem.Amazon;

/// <summary>
/// An enumeration of all possible CannedACLs that can be used
/// for S3 Buckets or S3 Objects. For more information about CannedACLs, refer to
/// <see href="http://docs.amazonwebservices.com/AmazonS3/latest/RESTAccessPolicy.html#RESTCannedAccessPolicies"/>.
/// </summary>
public enum AccessControl
{
/// <summary>
/// Owner gets FULL_CONTROL.
/// No one else has access rights (default).
/// </summary>
NoAcl,

/// <summary>
/// Owner gets FULL_CONTROL.
/// No one else has access rights (default).
/// </summary>
Private,

/// <summary>
/// Owner gets FULL_CONTROL and the anonymous principal is granted READ access.
/// If this policy is used on an object, it can be read from a browser with no authentication.
/// </summary>
PublicRead,

/// <summary>
/// Owner gets FULL_CONTROL, the anonymous principal is granted READ and WRITE access.
/// This can be a useful policy to apply to a bucket, but is generally not recommended.
/// </summary>
PublicReadWrite,

/// <summary>
/// Owner gets FULL_CONTROL, and any principal authenticated as a registered Amazon
/// S3 user is granted READ access.
/// </summary>
AuthenticatedRead,

/// <summary>
/// Owner gets FULL_CONTROL. Amazon EC2 gets READ access to GET an
/// Amazon Machine Image (AMI) bundle from Amazon S3.
/// </summary>
AwsExecRead,

/// <summary>
/// Object Owner gets FULL_CONTROL, Bucket Owner gets READ
/// This ACL applies only to objects and is equivalent to private when used with PUT Bucket.
/// You use this ACL to let someone other than the bucket owner write content (get full control)
/// in the bucket but still grant the bucket owner read access to the objects.
/// </summary>
BucketOwnerRead,

/// <summary>
/// Object Owner gets FULL_CONTROL, Bucket Owner gets FULL_CONTROL.
/// This ACL applies only to objects and is equivalent to private when used with PUT Bucket.
/// You use this ACL to let someone other than the bucket owner write content (get full control)
/// in the bucket but still grant the bucket owner full rights over the objects.
/// </summary>
BucketOwnerFullControl,

/// <summary>
/// The LogDelivery group gets WRITE and READ_ACP permissions on the bucket.
/// </summary>
LogDeliveryWrite
}
106 changes: 0 additions & 106 deletions src/Ramstack.FileSystem.Amazon/AmazonFile.cs

This file was deleted.

32 changes: 28 additions & 4 deletions src/Ramstack.FileSystem.Amazon/AmazonS3FileSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,14 @@ public AmazonS3FileSystem(string awsAccessKeyId, string awsSecretAccessKey, Regi
public VirtualFile GetFile(string path)
{
path = VirtualPath.GetFullPath(path);
return new AmazonFile(this, path);
return new S3File(this, path);
}

/// <inheritdoc />
public VirtualDirectory GetDirectory(string path)
{
path = VirtualPath.GetFullPath(path);
return new AmazonDirectory(this, path);
return new S3Directory(this, path);
}

/// <inheritdoc />
Expand All @@ -102,7 +102,18 @@ public void Dispose() =>
/// <returns>
/// A <see cref="ValueTask"/> representing the asynchronous operation.
/// </returns>
public async ValueTask CreateBucketAsync(CancellationToken cancellationToken = default)
public ValueTask CreateBucketAsync(CancellationToken cancellationToken = default) =>
CreateBucketAsync(AccessControl.NoAcl, cancellationToken);

/// <summary>
/// Creates the S3 bucket if it does not already exist.
/// </summary>
/// <param name="accessControl">The ACL to apply to the bucket.</param>
/// <param name="cancellationToken">A cancellation token to cancel the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> representing the asynchronous operation.
/// </returns>
public async ValueTask CreateBucketAsync(AccessControl accessControl, CancellationToken cancellationToken = default)
{
var exists = AmazonS3Util
.DoesS3BucketExistV2Async(AmazonClient, BucketName)
Expand All @@ -114,7 +125,20 @@ public async ValueTask CreateBucketAsync(CancellationToken cancellationToken = d
var request = new PutBucketRequest
{
BucketName = BucketName,
UseClientRegion = true
UseClientRegion = true,
CannedACL = accessControl switch
{
AccessControl.NoAcl => S3CannedACL.NoACL,
AccessControl.Private => S3CannedACL.Private,
AccessControl.PublicRead => S3CannedACL.PublicRead,
AccessControl.PublicReadWrite => S3CannedACL.PublicReadWrite,
AccessControl.AuthenticatedRead => S3CannedACL.AuthenticatedRead,
AccessControl.AwsExecRead => S3CannedACL.AWSExecRead,
AccessControl.BucketOwnerRead => S3CannedACL.BucketOwnerRead,
AccessControl.BucketOwnerFullControl => S3CannedACL.BucketOwnerFullControl,
AccessControl.LogDeliveryWrite => S3CannedACL.LogDeliveryWrite,
_ => throw new ArgumentOutOfRangeException(nameof(accessControl))
}
};

await AmazonClient
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace Ramstack.FileSystem.Amazon;
/// Represents an implementation of <see cref="VirtualDirectory"/> that maps a directory
/// to a path within a specified Amazon S3 bucket.
/// </summary>
internal sealed class AmazonDirectory : VirtualDirectory
internal sealed class S3Directory : VirtualDirectory
{
private readonly AmazonS3FileSystem _fs;
private readonly string _prefix;
Expand All @@ -19,11 +19,11 @@ internal sealed class AmazonDirectory : VirtualDirectory
public override IVirtualFileSystem FileSystem => _fs;

/// <summary>
/// Initializes a new instance of the <see cref="AmazonDirectory"/> class.
/// Initializes a new instance of the <see cref="S3Directory"/> class.
/// </summary>
/// <param name="fileSystem">The file system associated with this directory.</param>
/// <param name="path">The path to the directory within the specified Amazon S3 bucket.</param>
public AmazonDirectory(AmazonS3FileSystem fileSystem, string path) : base(path)
public S3Directory(AmazonS3FileSystem fileSystem, string path) : base(path)
{
_fs = fileSystem;
_prefix = FullName == "/" ? "" : $"{FullName[1..]}/";
Expand Down Expand Up @@ -96,10 +96,10 @@ protected override async IAsyncEnumerable<VirtualNode> GetFileNodesCoreAsync([En
.ConfigureAwait(false);

foreach (var prefix in response.CommonPrefixes)
yield return new AmazonDirectory(_fs, VirtualPath.Normalize(prefix));
yield return new S3Directory(_fs, VirtualPath.Normalize(prefix));

foreach (var obj in response.S3Objects)
yield return new AmazonFile(_fs, VirtualPath.Normalize(obj.Key));
yield return new S3File(_fs, VirtualPath.Normalize(obj.Key));

request.ContinuationToken = response.NextContinuationToken;
}
Expand Down
Loading

0 comments on commit 5acc1f0

Please sign in to comment.