diff --git a/sdk/storage/Azure.Storage.DataMovement.Files.Shares/api/Azure.Storage.DataMovement.Files.Shares.net6.0.cs b/sdk/storage/Azure.Storage.DataMovement.Files.Shares/api/Azure.Storage.DataMovement.Files.Shares.net6.0.cs index dfa95cd1db82d..1d35114f198b0 100644 --- a/sdk/storage/Azure.Storage.DataMovement.Files.Shares/api/Azure.Storage.DataMovement.Files.Shares.net6.0.cs +++ b/sdk/storage/Azure.Storage.DataMovement.Files.Shares/api/Azure.Storage.DataMovement.Files.Shares.net6.0.cs @@ -23,5 +23,16 @@ public ShareFilesStorageResourceProvider(Azure.Storage.StorageSharedKeyCredentia public partial class ShareFileStorageResourceOptions { public ShareFileStorageResourceOptions() { } + public bool? Archive { get { throw null; } set { } } + public Azure.Storage.Files.Shares.Models.ShareFileRequestConditions DestinationConditions { get { throw null; } set { } } + public System.Collections.Generic.IDictionary DirectoryMetadata { get { throw null; } set { } } + public Azure.Storage.DownloadTransferValidationOptions DownloadTransferValidationOptions { get { throw null; } set { } } + public System.Collections.Generic.IDictionary FileMetadata { get { throw null; } set { } } + public string FilePermissions { get { throw null; } set { } } + public Azure.Storage.Files.Shares.Models.ShareFileHttpHeaders HttpHeaders { get { throw null; } set { } } + public Azure.Storage.Files.Shares.Models.ShareProtocols? Protocols { get { throw null; } set { } } + public Azure.Storage.Files.Shares.Models.FileSmbProperties SmbProperties { get { throw null; } set { } } + public Azure.Storage.Files.Shares.Models.ShareFileRequestConditions SourceConditions { get { throw null; } set { } } + public Azure.Storage.UploadTransferValidationOptions UploadTransferValidationOptions { get { throw null; } set { } } } } diff --git a/sdk/storage/Azure.Storage.DataMovement.Files.Shares/api/Azure.Storage.DataMovement.Files.Shares.netstandard2.0.cs b/sdk/storage/Azure.Storage.DataMovement.Files.Shares/api/Azure.Storage.DataMovement.Files.Shares.netstandard2.0.cs index dfa95cd1db82d..1d35114f198b0 100644 --- a/sdk/storage/Azure.Storage.DataMovement.Files.Shares/api/Azure.Storage.DataMovement.Files.Shares.netstandard2.0.cs +++ b/sdk/storage/Azure.Storage.DataMovement.Files.Shares/api/Azure.Storage.DataMovement.Files.Shares.netstandard2.0.cs @@ -23,5 +23,16 @@ public ShareFilesStorageResourceProvider(Azure.Storage.StorageSharedKeyCredentia public partial class ShareFileStorageResourceOptions { public ShareFileStorageResourceOptions() { } + public bool? Archive { get { throw null; } set { } } + public Azure.Storage.Files.Shares.Models.ShareFileRequestConditions DestinationConditions { get { throw null; } set { } } + public System.Collections.Generic.IDictionary DirectoryMetadata { get { throw null; } set { } } + public Azure.Storage.DownloadTransferValidationOptions DownloadTransferValidationOptions { get { throw null; } set { } } + public System.Collections.Generic.IDictionary FileMetadata { get { throw null; } set { } } + public string FilePermissions { get { throw null; } set { } } + public Azure.Storage.Files.Shares.Models.ShareFileHttpHeaders HttpHeaders { get { throw null; } set { } } + public Azure.Storage.Files.Shares.Models.ShareProtocols? Protocols { get { throw null; } set { } } + public Azure.Storage.Files.Shares.Models.FileSmbProperties SmbProperties { get { throw null; } set { } } + public Azure.Storage.Files.Shares.Models.ShareFileRequestConditions SourceConditions { get { throw null; } set { } } + public Azure.Storage.UploadTransferValidationOptions UploadTransferValidationOptions { get { throw null; } set { } } } } diff --git a/sdk/storage/Azure.Storage.DataMovement.Files.Shares/samples/Azure.Storage.DataMovement.Files.Shares.Samples.Tests.csproj b/sdk/storage/Azure.Storage.DataMovement.Files.Shares/samples/Azure.Storage.DataMovement.Files.Shares.Samples.Tests.csproj index 04a9f3bf28eda..f65fd46641aea 100644 --- a/sdk/storage/Azure.Storage.DataMovement.Files.Shares/samples/Azure.Storage.DataMovement.Files.Shares.Samples.Tests.csproj +++ b/sdk/storage/Azure.Storage.DataMovement.Files.Shares/samples/Azure.Storage.DataMovement.Files.Shares.Samples.Tests.csproj @@ -9,4 +9,4 @@ PreserveNewest - + \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.DataMovement.Files.Shares/src/Azure.Storage.DataMovement.Files.Shares.csproj b/sdk/storage/Azure.Storage.DataMovement.Files.Shares/src/Azure.Storage.DataMovement.Files.Shares.csproj index 239be80bfc73d..8d4bed65f1754 100644 --- a/sdk/storage/Azure.Storage.DataMovement.Files.Shares/src/Azure.Storage.DataMovement.Files.Shares.csproj +++ b/sdk/storage/Azure.Storage.DataMovement.Files.Shares/src/Azure.Storage.DataMovement.Files.Shares.csproj @@ -1,12 +1,12 @@ - + $(RequiredTargetFrameworks);net6.0 true - Microsoft Azure.Storage.DataMovement.Blobs client library + Microsoft Azure.Storage.DataMovement.Files.Shares client library 12.0.0-beta.1 - FileDataMovementSDK;$(DefineConstants) + ShareDataMovementSDK;$(DefineConstants) Microsoft Azure Storage DataMovement, DataMovement, Microsoft, Azure, StorageScalable, azureofficial This client library enables working with the Microsoft Azure Storage services which include the blob and file services for storing binary and text data, and the queue service for storing messages that may be accessed by a client. @@ -32,6 +32,8 @@ + + diff --git a/sdk/storage/Azure.Storage.DataMovement.Files.Shares/src/DataMovementShareConstants.cs b/sdk/storage/Azure.Storage.DataMovement.Files.Shares/src/DataMovementShareConstants.cs new file mode 100644 index 0000000000000..a0c773c0901df --- /dev/null +++ b/sdk/storage/Azure.Storage.DataMovement.Files.Shares/src/DataMovementShareConstants.cs @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Azure.Storage.DataMovement.Files.Shares +{ + internal class DataMovementShareConstants + { + public const int KB = 1024; + public const int MB = KB * 1024; + + internal const int MaxRange = 4 * MB; + } +} diff --git a/sdk/storage/Azure.Storage.DataMovement.Files.Shares/src/DataMovementSharesExtensions.cs b/sdk/storage/Azure.Storage.DataMovement.Files.Shares/src/DataMovementSharesExtensions.cs new file mode 100644 index 0000000000000..f3a6d2378f092 --- /dev/null +++ b/sdk/storage/Azure.Storage.DataMovement.Files.Shares/src/DataMovementSharesExtensions.cs @@ -0,0 +1,103 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using Azure.Storage.Files.Shares.Models; + +namespace Azure.Storage.DataMovement.Files.Shares +{ + internal static partial class DataMovementSharesExtensions + { + internal static ShareFileUploadOptions ToShareFileUploadOptions( + this ShareFileStorageResourceOptions options) + => new() + { + Conditions = options?.DestinationConditions, + TransferValidation = options?.UploadTransferValidationOptions, + }; + + internal static ShareFileUploadRangeOptions ToShareFileUploadRangeOptions( + this ShareFileStorageResourceOptions options) + => new() + { + Conditions = options?.DestinationConditions, + TransferValidation = options?.UploadTransferValidationOptions, + }; + + internal static ShareFileUploadRangeFromUriOptions ToShareFileUploadRangeFromUriOptions( + this ShareFileStorageResourceOptions options) + => new() + { + Conditions = options?.DestinationConditions, + }; + + internal static StorageResourceProperties ToStorageResourceProperties( + this ShareFileProperties properties) + => new( + lastModified: properties.LastModified, + createdOn: properties?.SmbProperties?.FileCreatedOn ?? default, + metadata: properties?.Metadata, + copyCompletedOn: properties.CopyCompletedOn, + copyStatusDescription: properties?.CopyStatusDescription, + copyId: properties?.CopyId, + copyProgress: properties?.CopyProgress, + copySource: new Uri(properties?.CopySource), + contentLength: properties.ContentLength, + contentType: properties?.ContentType, + eTag: properties.ETag, + contentHash: properties?.ContentHash, + blobSequenceNumber: default, + blobCommittedBlockCount: default, + isServerEncrypted: properties.IsServerEncrypted, + encryptionKeySha256: default, + encryptionScope: default, + versionId: default, + isLatestVersion: default, + expiresOn: default, + lastAccessed: default); + + internal static ShareFileDownloadOptions ToShareFileDownloadOptions( + this ShareFileStorageResourceOptions options, + HttpRange range) + => new() + { + Range = range, + Conditions = options?.SourceConditions, + TransferValidation = options?.DownloadTransferValidationOptions, + }; + + internal static StorageResourceReadStreamResult ToStorageResourceReadStreamResult( + this ShareFileDownloadInfo info) + => new( + content: info?.Content, + contentRange: info?.Details.ContentRange, + acceptRanges: info?.Details.AcceptRanges, + rangeContentHash: info?.Details.FileContentHash, + properties: info?.Details.ToStorageResourceProperties()); + + private static StorageResourceProperties ToStorageResourceProperties( + this ShareFileDownloadDetails details) + => new( + lastModified: details.LastModified, + createdOn: details.SmbProperties.FileCreatedOn ?? default, + metadata: details.Metadata, + copyCompletedOn: details.CopyCompletedOn, + copyStatusDescription: details.CopyStatusDescription, + copyId: details.CopyId, + copyProgress: details.CopyProgress, + copySource: details.CopySource, + contentLength: details.ContentRange.Length, + contentType: default, + eTag: details.ETag, + contentHash: default, + blobSequenceNumber: default, + blobCommittedBlockCount: default, + isServerEncrypted: details.IsServerEncrypted, + encryptionKeySha256: default, + encryptionScope: default, + versionId: default, + isLatestVersion: default, + expiresOn: default, + lastAccessed: default); + } +} diff --git a/sdk/storage/Azure.Storage.DataMovement.Files.Shares/src/ShareDirectoryStorageResourceContainer.cs b/sdk/storage/Azure.Storage.DataMovement.Files.Shares/src/ShareDirectoryStorageResourceContainer.cs index af427815b399f..9a3ca0124f4ea 100644 --- a/sdk/storage/Azure.Storage.DataMovement.Files.Shares/src/ShareDirectoryStorageResourceContainer.cs +++ b/sdk/storage/Azure.Storage.DataMovement.Files.Shares/src/ShareDirectoryStorageResourceContainer.cs @@ -35,7 +35,7 @@ protected override StorageResourceItem GetStorageResourceReference(string path) dir = dir.GetSubdirectoryClient(pathSegment); } ShareFileClient file = dir.GetFileClient(pathSegments.Last()); - return new ShareFileStorageResourceItem(file, ResourceOptions); + return new ShareFileStorageResource(file, ResourceOptions); } protected override async IAsyncEnumerable GetStorageResourcesAsync( @@ -44,7 +44,7 @@ protected override async IAsyncEnumerable GetStorageResourcesAs await foreach (ShareFileClient client in PathScanner.ScanFilesAsync( ShareDirectoryClient, cancellationToken).ConfigureAwait(false)) { - yield return new ShareFileStorageResourceItem(client, ResourceOptions); + yield return new ShareFileStorageResource(client, ResourceOptions); } } } diff --git a/sdk/storage/Azure.Storage.DataMovement.Files.Shares/src/ShareFileStorageResource.cs b/sdk/storage/Azure.Storage.DataMovement.Files.Shares/src/ShareFileStorageResource.cs new file mode 100644 index 0000000000000..dd06ea2e7bedc --- /dev/null +++ b/sdk/storage/Azure.Storage.DataMovement.Files.Shares/src/ShareFileStorageResource.cs @@ -0,0 +1,174 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Azure.Core; +using Azure.Storage.Files.Shares; +using Azure.Storage.Files.Shares.Models; + +namespace Azure.Storage.DataMovement.Files.Shares +{ + internal class ShareFileStorageResource : StorageResourceItemInternal + { + internal long? _length; + internal readonly ShareFileStorageResourceOptions _options; + internal ETag? _etagDownloadLock = default; + + internal ShareFileClient ShareFileClient { get; } + + public override Uri Uri => ShareFileClient.Uri; + + protected override string ResourceId => "ShareFile"; + + protected override DataTransferOrder TransferType => DataTransferOrder.Sequential; + + protected override long MaxChunkSize => DataMovementShareConstants.MaxRange; + + protected override long? Length => _length; + + public ShareFileStorageResource( + ShareFileClient fileClient, + ShareFileStorageResourceOptions options = default) + { + ShareFileClient = fileClient; + _options = options; + } + + /// + /// Internal Constructor for constructing the resource retrieved by a GetStorageResources. + /// + /// The blob client which will service the storage resource operations. + /// The content length of the blob. + /// Preset etag to lock on for reads. + /// Options for the storage resource. See . + internal ShareFileStorageResource( + ShareFileClient fileClient, + long? length, + ETag? etagLock, + ShareFileStorageResourceOptions options = default) + : this(fileClient, options) + { + _length = length; + _etagDownloadLock = etagLock; + } + + protected override Task CompleteTransferAsync( + bool overwrite, + CancellationToken cancellationToken = default) + { + CancellationHelper.ThrowIfCancellationRequested(cancellationToken); + return Task.CompletedTask; + } + + protected override async Task CopyBlockFromUriAsync( + StorageResourceItem sourceResource, + HttpRange range, + bool overwrite, + long completeLength, + StorageResourceCopyFromUriOptions options = null, + CancellationToken cancellationToken = default) + { + CancellationHelper.ThrowIfCancellationRequested(cancellationToken); + await ShareFileClient.UploadRangeFromUriAsync( + sourceUri: sourceResource.Uri, + range: range, + sourceRange: range, + options: _options.ToShareFileUploadRangeFromUriOptions(), + cancellationToken: cancellationToken).ConfigureAwait(false); + } + + protected override async Task CopyFromStreamAsync( + Stream stream, + long streamLength, + bool overwrite, + long completeLength, + StorageResourceWriteToOffsetOptions options = null, + CancellationToken cancellationToken = default) + { + CancellationHelper.ThrowIfCancellationRequested(cancellationToken); + + long position = options?.Position != default ? options.Position.Value : 0; + if ((streamLength == completeLength) && position == 0) + { + // Default to Upload + await ShareFileClient.UploadAsync( + stream, + _options.ToShareFileUploadOptions(), + cancellationToken: cancellationToken).ConfigureAwait(false); + return; + } + + // Otherwise upload the Range + await ShareFileClient.UploadRangeAsync( + new HttpRange(position, streamLength), + stream, + _options.ToShareFileUploadRangeOptions(), + cancellationToken).ConfigureAwait(false); + } + + protected override async Task CopyFromUriAsync( + StorageResourceItem sourceResource, + bool overwrite, + long completeLength, + StorageResourceCopyFromUriOptions options = null, + CancellationToken cancellationToken = default) + { + CancellationHelper.ThrowIfCancellationRequested(cancellationToken); + await ShareFileClient.UploadRangeFromUriAsync( + sourceUri: sourceResource.Uri, + range: new HttpRange(0, completeLength), + sourceRange: new HttpRange(0, completeLength), + options: _options.ToShareFileUploadRangeFromUriOptions(), + cancellationToken: cancellationToken).ConfigureAwait(false); + } + + protected override async Task DeleteIfExistsAsync(CancellationToken cancellationToken = default) + { + CancellationHelper.ThrowIfCancellationRequested(cancellationToken); + return await ShareFileClient.DeleteIfExistsAsync(cancellationToken: cancellationToken).ConfigureAwait(false); + } + + protected override Task GetCopyAuthorizationHeaderAsync(CancellationToken cancellationToken = default) + { + CancellationHelper.ThrowIfCancellationRequested(cancellationToken); + // TODO: This needs an update to ShareFileClient to allow getting the Copy Authorization Token + throw new NotImplementedException(); + } + + protected override async Task GetPropertiesAsync(CancellationToken cancellationToken = default) + { + CancellationHelper.ThrowIfCancellationRequested(cancellationToken); + Response response = await ShareFileClient.GetPropertiesAsync( + conditions: _options?.SourceConditions, + cancellationToken: cancellationToken).ConfigureAwait(false); + // TODO: should we be grabbing the ETag here even though we can't apply it to the download. + //GrabEtag(response.GetRawResponse()); + return response.Value.ToStorageResourceProperties(); + } + + protected override async Task ReadStreamAsync( + long position = 0, + long? length = null, + CancellationToken cancellationToken = default) + { + CancellationHelper.ThrowIfCancellationRequested(cancellationToken); + Response response = await ShareFileClient.DownloadAsync( + _options.ToShareFileDownloadOptions(new HttpRange(position, length)), + cancellationToken).ConfigureAwait(false); + return response.Value.ToStorageResourceReadStreamResult(); + } + + protected override StorageResourceCheckpointData GetSourceCheckpointData() + { + throw new NotImplementedException(); + } + + protected override StorageResourceCheckpointData GetDestinationCheckpointData() + { + throw new NotImplementedException(); + } + } +} diff --git a/sdk/storage/Azure.Storage.DataMovement.Files.Shares/src/ShareFileStorageResourceItem.cs b/sdk/storage/Azure.Storage.DataMovement.Files.Shares/src/ShareFileStorageResourceItem.cs deleted file mode 100644 index ac10eafe4d8f9..0000000000000 --- a/sdk/storage/Azure.Storage.DataMovement.Files.Shares/src/ShareFileStorageResourceItem.cs +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using Azure.Storage.Files.Shares; - -namespace Azure.Storage.DataMovement.Files.Shares -{ - internal class ShareFileStorageResourceItem : StorageResourceItem - { - internal readonly ShareFileStorageResourceOptions _options; - - internal ShareFileClient ShareFileClient { get; } - - public override Uri Uri => ShareFileClient.Uri; - - protected override string ResourceId => throw new NotImplementedException(); - - protected override DataTransferOrder TransferType => throw new NotImplementedException(); - - protected override long MaxChunkSize => throw new NotImplementedException(); - - protected override long? Length => throw new NotImplementedException(); - - public ShareFileStorageResourceItem( - ShareFileClient fileClient, - ShareFileStorageResourceOptions options = default) - { - ShareFileClient = fileClient; - _options = options; - } - - protected override Task CompleteTransferAsync(bool overwrite, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - protected override Task CopyBlockFromUriAsync(StorageResourceItem sourceResource, HttpRange range, bool overwrite, long completeLength, StorageResourceCopyFromUriOptions options = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - protected override Task CopyFromStreamAsync(Stream stream, long streamLength, bool overwrite, long completeLength, StorageResourceWriteToOffsetOptions options = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - protected override Task CopyFromUriAsync(StorageResourceItem sourceResource, bool overwrite, long completeLength, StorageResourceCopyFromUriOptions options = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - protected override Task DeleteIfExistsAsync(CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - protected override Task GetCopyAuthorizationHeaderAsync(CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - protected override Task GetPropertiesAsync(CancellationToken token = default) - { - throw new NotImplementedException(); - } - - protected override Task ReadStreamAsync(long position = 0, long? length = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - protected override StorageResourceCheckpointData GetSourceCheckpointData() - { - throw new NotImplementedException(); - } - - protected override StorageResourceCheckpointData GetDestinationCheckpointData() - { - throw new NotImplementedException(); - } - } -} diff --git a/sdk/storage/Azure.Storage.DataMovement.Files.Shares/src/ShareFileStorageResourceOptions.cs b/sdk/storage/Azure.Storage.DataMovement.Files.Shares/src/ShareFileStorageResourceOptions.cs index 9e9b5dbf5f16a..0ef6cf4ff3f6d 100644 --- a/sdk/storage/Azure.Storage.DataMovement.Files.Shares/src/ShareFileStorageResourceOptions.cs +++ b/sdk/storage/Azure.Storage.DataMovement.Files.Shares/src/ShareFileStorageResourceOptions.cs @@ -1,6 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +using Azure.Storage.Files.Shares.Models; +using Metadata = System.Collections.Generic.IDictionary; + namespace Azure.Storage.DataMovement.Files.Shares { /// @@ -9,10 +12,99 @@ namespace Azure.Storage.DataMovement.Files.Shares public class ShareFileStorageResourceOptions { /// - /// Default constructor. + /// Optional SMB properties to set for the directory and/or file resource. + /// + public FileSmbProperties SmbProperties { get; set; } + + /// + /// Optional. The file permission to set on the destination directory and/or file. + /// + /// Applies to copy and upload transfers. + /// TODO: determine whether this is something we want to apply and override + /// the potential default of copying over permissions when we go to do + /// file share to file share copy transfer. + /// + public string FilePermissions { get; set; } + + /// + /// Optional SMB properties to set for the directory and/or file. + /// + public ShareFileHttpHeaders HttpHeaders { get; set; } + + /// + /// Optional. See . + /// Access conditions on the copying of data from this source storage resource share file. + /// + /// Applies to copy and download transfers. + /// + public ShareFileRequestConditions SourceConditions { get; set; } + + /// + /// Optional. See . + /// Access conditions on the copying of data to this share file. + /// + /// Applies to copy and upload transfers. + /// + public ShareFileRequestConditions DestinationConditions { get; set; } + + /// + /// Optional boolean Specifying to set archive attribute on a target file. True + /// means archive attribute will be set on a target file despite attribute + /// overrides or a source file state. + /// + /// Only valid on Service Copy on the destination. + /// + public bool? Archive { get; set; } + + /// + /// Optional. Defines custom metadata to set on the destination resource. + /// + /// Applies to upload and copy transfers. + /// +#pragma warning disable CA2227 // Collection properties should be readonly + public Metadata DirectoryMetadata { get; set; } +#pragma warning restore CA2227 // Collection properties should be readonly + + /// + /// Optional. Defines custom metadata to set on the destination resource. + /// + /// Applies to upload and copy transfers. + /// +#pragma warning disable CA2227 // Collection properties should be readonly + public Metadata FileMetadata { get; set; } +#pragma warning restore CA2227 // Collection properties should be readonly + + /// + /// The protocols to enable for the share. + /// + public ShareProtocols? Protocols { get; set; } + + /// + /// Optional. Options for transfer validation settings on this operation. + /// When transfer validation options are set in the client, setting this parameter + /// acts as an override. + /// This operation does not allow + /// to be set. + /// + /// Applies to upload transfers. + /// + public UploadTransferValidationOptions UploadTransferValidationOptions { get; set; } + + /// + /// Optional. Options for transfer validation settings on this operation. + /// When transfer validation options are set in the client, setting this parameter + /// acts as an override. + /// Set to false if you + /// would like to skip SDK checksum validation and validate the checksum found + /// in the object yourself. + /// Range must be provided explicitly, stating a range withing Azure + /// Storage size limits for requesting a transactional hash. See the + /// + /// REST documentation for range limitation details. + /// + /// Applies to download transfers. /// - public ShareFileStorageResourceOptions() - { - } + public DownloadTransferValidationOptions DownloadTransferValidationOptions + { get; set; } } } diff --git a/sdk/storage/Azure.Storage.DataMovement.Files.Shares/src/ShareFilesStorageResourceProvider.cs b/sdk/storage/Azure.Storage.DataMovement.Files.Shares/src/ShareFilesStorageResourceProvider.cs index 48a92f0ae9825..c832b48d3e27c 100644 --- a/sdk/storage/Azure.Storage.DataMovement.Files.Shares/src/ShareFilesStorageResourceProvider.cs +++ b/sdk/storage/Azure.Storage.DataMovement.Files.Shares/src/ShareFilesStorageResourceProvider.cs @@ -304,7 +304,7 @@ public StorageResource FromFile(string fileUri, ShareFileStorageResourceOptions CredentialType.Sas => new ShareFileClient(new Uri(fileUri), _getAzureSasCredential(fileUri, false)), _ => throw BadCredentialTypeException(_credentialType), }; - return new ShareFileStorageResourceItem(client, options); + return new ShareFileStorageResource(client, options); } #endregion @@ -344,7 +344,7 @@ public StorageResource FromClient( ShareFileClient client, ShareFileStorageResourceOptions options = default) { - return new ShareFileStorageResourceItem(client, options); + return new ShareFileStorageResource(client, options); } #endregion diff --git a/sdk/storage/Azure.Storage.DataMovement.Files.Shares/tests/Azure.Storage.DataMovement.Files.Shares.Tests.csproj b/sdk/storage/Azure.Storage.DataMovement.Files.Shares/tests/Azure.Storage.DataMovement.Files.Shares.Tests.csproj index cdd82082b4333..eeca6c590db6f 100644 --- a/sdk/storage/Azure.Storage.DataMovement.Files.Shares/tests/Azure.Storage.DataMovement.Files.Shares.Tests.csproj +++ b/sdk/storage/Azure.Storage.DataMovement.Files.Shares/tests/Azure.Storage.DataMovement.Files.Shares.Tests.csproj @@ -1,33 +1,41 @@  $(RequiredTargetFrameworks) - Microsoft Azure.Storage.DataMovement.Files.Shareas client library tests - BlobDataMovementSDK;$(DefineConstants) + Microsoft Azure.Storage.DataMovement.Files.Shares client library tests + ShareDataMovementSDK;$(DefineConstants) false - DMBlobs + DMShare + + + + + + - - - - - + + + + + + + PreserveNewest - - - PreserveNewest - + + + PreserveNewest + diff --git a/sdk/storage/Azure.Storage.DataMovement.Files.Shares/tests/ShareDirectoryStorageResourceContainerTests.cs b/sdk/storage/Azure.Storage.DataMovement.Files.Shares/tests/ShareDirectoryStorageResourceContainerTests.cs index ddd8c483a04bb..327b072cd6376 100644 --- a/sdk/storage/Azure.Storage.DataMovement.Files.Shares/tests/ShareDirectoryStorageResourceContainerTests.cs +++ b/sdk/storage/Azure.Storage.DataMovement.Files.Shares/tests/ShareDirectoryStorageResourceContainerTests.cs @@ -51,8 +51,8 @@ public async Task GetStorageResourcesCallsPathScannerCorrectly() List results = new(); await foreach (StorageResource res in resource.GetStorageResourcesInternalAsync()) { - Assert.That(res, Is.TypeOf(typeof(ShareFileStorageResourceItem))); - results.Add((res as ShareFileStorageResourceItem).ShareFileClient); + Assert.That(res, Is.TypeOf(typeof(ShareFileStorageResource))); + results.Add((res as ShareFileStorageResource).ShareFileClient); } Assert.That(results, Is.EquivalentTo(expectedFiles.Select(mock => mock.Object))); } @@ -73,8 +73,8 @@ public void GetCorrectStorageResourceItem() StorageResourceItem resourceItem = resourceContainer.GetStorageResourceReferenceInternal( string.Join("/", pathSegments)); - Assert.That(resourceItem, Is.TypeOf(typeof(ShareFileStorageResourceItem))); - ShareFileStorageResourceItem fileResourceItem = resourceItem as ShareFileStorageResourceItem; + Assert.That(resourceItem, Is.TypeOf(typeof(ShareFileStorageResource))); + ShareFileStorageResource fileResourceItem = resourceItem as ShareFileStorageResource; Assert.That(fileResourceItem.ShareFileClient.Path, Is.EqualTo(startingDir.Path + "/" + string.Join("/", pathSegments))); Assert.That(fileResourceItem.ShareFileClient.Name, Is.EqualTo(pathSegments.Last())); diff --git a/sdk/storage/Azure.Storage.DataMovement.Files.Shares/tests/ShareFileResourceTests.cs b/sdk/storage/Azure.Storage.DataMovement.Files.Shares/tests/ShareFileResourceTests.cs new file mode 100644 index 0000000000000..fd2bc19fc5968 --- /dev/null +++ b/sdk/storage/Azure.Storage.DataMovement.Files.Shares/tests/ShareFileResourceTests.cs @@ -0,0 +1,484 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Azure.Core.TestFramework; +using Azure.Storage.Files.Shares; +using Azure.Storage.Files.Shares.Models; +using Azure.Storage.Test; +using Moq; +using NUnit.Framework; + +namespace Azure.Storage.DataMovement.Files.Shares.Tests +{ + public class ShareFileResourceTests + { + public static byte[] GetRandomBuffer(long size, Random random = null) + { + random ??= new Random(Environment.TickCount); + var buffer = new byte[size]; + random.NextBytes(buffer); + return buffer; + } + + [Test] + public void Ctor_PublicUri() + { + // Arrange + Uri uri = new Uri("https://storageaccount.blob.core.windows.net/"); + ShareFileClient fileClient = new ShareFileClient(uri); + ShareFileStorageResource storageResource = new(fileClient); + + // Assert + Assert.AreEqual(uri, storageResource.Uri.AbsoluteUri); + } + + [Test] + public async Task ReadStreamAsync() + { + // Arrange + Mock mock = new( + new Uri("https://storageaccount.file.core.windows.net/container/file"), + new ShareClientOptions()); + int length = 1024; + string contentRange = "bytes 0-1024/1024"; + var data = GetRandomBuffer(length); + using (var stream = new MemoryStream(data)) + { + mock.Setup(b => b.DownloadAsync(It.IsAny(), It.IsAny())) + .Returns(Task.FromResult(Response.FromValue( + FilesModelFactory.StorageFileDownloadInfo( + content: stream, + contentLength: length, + contentRange: contentRange), + new MockResponse(201)))); + + ShareFileStorageResource storageResource = new ShareFileStorageResource(mock.Object); + + // Act + StorageResourceReadStreamResult result = await storageResource.ReadStreamInternalAsync(); + + // Assert + Assert.NotNull(result); + Assert.That(data, Is.EqualTo(result.Content.AsBytes().ToArray())); + } + mock.Verify(b => b.DownloadAsync(It.IsAny(), It.IsAny()), + Times.Once()); + mock.VerifyNoOtherCalls(); + } + + [Test] + public async Task ReadStreamAsync_Position() + { + // Arrange + Mock mock = new( + new Uri("https://storageaccount.file.core.windows.net/container/file"), + new ShareClientOptions()); + int position = 5; + int length = 1024; + string contentRange = "bytes 0-1024/1024"; + var data = GetRandomBuffer(length); + using (var stream = new MemoryStream(data)) + { + mock.Setup(b => b.DownloadAsync(It.IsAny(), It.IsAny())) + .Returns(Task.FromResult(Response.FromValue( + FilesModelFactory.StorageFileDownloadInfo( + content: stream, + contentLength: length, + contentRange: contentRange), + new MockResponse(201)))); + + ShareFileStorageResource storageResource = new ShareFileStorageResource(mock.Object); + + // Act + StorageResourceReadStreamResult result = await storageResource.ReadStreamInternalAsync(); + + // Assert + Assert.NotNull(result); + byte[] dataAt5 = new byte[data.Length - position]; + Array.Copy(data, position, dataAt5, 0, data.Length - position); + Assert.That(data, Is.EqualTo(result.Content.AsBytes().ToArray())); + } + mock.Verify(b => b.DownloadAsync(It.IsAny(), It.IsAny()), + Times.Once()); + mock.VerifyNoOtherCalls(); + } + + [Test] + public async Task ReadStreamAsync_Error() + { + // Arrange + Mock mock = new( + new Uri("https://storageaccount.file.core.windows.net/container/file"), + new ShareClientOptions()); + + mock.Setup(b => b.DownloadAsync(It.IsAny(), It.IsAny())) + .Throws(new RequestFailedException(status: 404, message: "The specified resource does not exist.", errorCode: "ResourceNotFound", default)); + + ShareFileStorageResource storageResource = new ShareFileStorageResource(mock.Object); + + // Act without creating the blob + await TestHelper.AssertExpectedExceptionAsync( + storageResource.ReadStreamInternalAsync(), + e => + { + Assert.AreEqual("ResourceNotFound", e.ErrorCode); + }); + mock.Verify(b => b.DownloadAsync(It.IsAny(), It.IsAny()), + Times.Once()); + mock.VerifyNoOtherCalls(); + } + + [Test] + public async Task CopyFromStreamAsync() + { + // Arrange + Mock mock = new( + new Uri("https://storageaccount.file.core.windows.net/container/file"), + new ShareClientOptions()); + int length = 1024; + var data = GetRandomBuffer(length); + using var stream = new MemoryStream(data); + using var fileContentStream = new MemoryStream(); + mock.Setup(b => b.UploadAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .Callback( + async (uploadedstream, options, token) => + { + await uploadedstream.CopyToAsync(fileContentStream).ConfigureAwait(false); + fileContentStream.Position = 0; + }) + .Returns(Task.FromResult(Response.FromValue( + ShareModelFactory.ShareFileUploadInfo( + eTag: new ETag("eTag"), + lastModified: DateTimeOffset.UtcNow, + contentHash: default, + isServerEncrypted: false), + new MockResponse(200)))); + + ShareFileStorageResource storageResource = new ShareFileStorageResource(mock.Object); + + // Act + await storageResource.CopyFromStreamInternalAsync( + stream: stream, + streamLength: length, + overwrite: false, + completeLength: length); + + Assert.That(data, Is.EqualTo(fileContentStream.AsBytes().ToArray())); + mock.Verify(b => b.UploadAsync(It.IsAny(), It.IsAny(), It.IsAny()), + Times.Once()); + mock.VerifyNoOtherCalls(); + } + + [Test] + public async Task CopyFromStreamAsync_Position() + { + // Arrange + Mock mock = new( + new Uri("https://storageaccount.file.core.windows.net/container/file"), + new ShareClientOptions()); + int position = 5; + int length = 1024; + var data = GetRandomBuffer(length); + using var stream = new MemoryStream(data); + using var fileContentStream = new MemoryStream(); + mock.Setup(b => b.UploadRangeAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Callback( + async (range, uploadedstream, options, token) => + { + fileContentStream.Position = 5; + await uploadedstream.CopyToAsync(fileContentStream).ConfigureAwait(false); + fileContentStream.Position = 0; + }) + .Returns(Task.FromResult(Response.FromValue( + ShareModelFactory.ShareFileUploadInfo( + eTag: new ETag("eTag"), + lastModified: DateTimeOffset.UtcNow, + contentHash: default, + isServerEncrypted: false), + new MockResponse(200)))); + + ShareFileStorageResource storageResource = new ShareFileStorageResource(mock.Object); + + // Act + await storageResource.CopyFromStreamInternalAsync( + stream: stream, + streamLength: length, + overwrite: false, + completeLength: length, + options: new StorageResourceWriteToOffsetOptions() { Position = position }); + + // Assert + byte[] dataAt5 = new byte[data.Length + position]; + Array.Copy(data, 0, dataAt5, 5, length); + Assert.That(dataAt5, Is.EqualTo(fileContentStream.AsBytes().ToArray())); + mock.Verify(b => b.UploadRangeAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), + Times.Once()); + mock.VerifyNoOtherCalls(); + } + + [Test] + public async Task CopyFromStreamAsync_Error() + { + // Arrange + Mock mock = new( + new Uri("https://storageaccount.file.core.windows.net/container/file"), + new ShareClientOptions()); + + mock.Setup(b => b.UploadAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .Throws(new RequestFailedException(status: 404, message: "The specified resource does not exist.", errorCode: "ResourceNotFound", default)); + + ShareFileStorageResource storageResource = new ShareFileStorageResource(mock.Object); + + // Act + int length = 1024; + var data = GetRandomBuffer(length); + using (var stream = new MemoryStream(data)) + { + await TestHelper.AssertExpectedExceptionAsync( + storageResource.CopyFromStreamInternalAsync(stream, length, false, length), + e => + { + Assert.AreEqual("ResourceNotFound", e.ErrorCode); + }); + } + mock.Verify(b => b.UploadAsync(It.IsAny(), It.IsAny(), It.IsAny()), + Times.Once()); + mock.VerifyNoOtherCalls(); + } + + [Test] + public async Task CopyFromUriAsync() + { + // Arrange + Mock sourceResource = new(); + sourceResource.Setup(b => b.Uri) + .Returns(new Uri("https://storageaccount.file.core.windows.net/container/sourcefile")); + + Mock mockDestination = new( + new Uri("https://storageaccount.file.core.windows.net/container/destinationfile"), + new ShareClientOptions()); + + mockDestination.Setup(b => b.UploadRangeFromUriAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(Task.FromResult(Response.FromValue( + ShareModelFactory.ShareFileUploadInfo( + eTag: new ETag("eTag"), + lastModified: DateTimeOffset.UtcNow, + contentHash: default, + isServerEncrypted: false), + new MockResponse(200)))); + ShareFileStorageResource destinationResource = new ShareFileStorageResource(mockDestination.Object); + + int length = 1024; + await destinationResource.CopyFromUriInternalAsync(sourceResource.Object, false, length); + + sourceResource.Verify(b => b.Uri, Times.Once()); + sourceResource.VerifyNoOtherCalls(); + mockDestination.Verify(b => b.UploadRangeFromUriAsync( + sourceResource.Object.Uri, + new HttpRange(0, length), + new HttpRange(0, length), + It.IsAny(), + It.IsAny()), + Times.Once()); + mockDestination.VerifyNoOtherCalls(); + } + + [Test] + public async Task CopyFromUriAsync_Error() + { + // Arrange + Mock sourceResource = new(); + sourceResource.Setup(b => b.Uri) + .Returns(new Uri("https://storageaccount.file.core.windows.net/container/sourcefile")); + + Mock mockDestination = new( + new Uri("https://storageaccount.file.core.windows.net/container/destinationfile"), + new ShareClientOptions()); + + mockDestination.Setup(b => b.UploadRangeFromUriAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Throws(new RequestFailedException(status: 404, message: "The specified resource does not exist.", errorCode: "ResourceNotFound", default)); + ShareFileStorageResource destinationResource = new ShareFileStorageResource(mockDestination.Object); + + // Act + int length = 1024; + await TestHelper.AssertExpectedExceptionAsync( + destinationResource.CopyFromUriInternalAsync(sourceResource.Object, false, length), + e => + { + Assert.AreEqual("ResourceNotFound", e.ErrorCode); + }); + + sourceResource.Verify(b => b.Uri, Times.Once()); + sourceResource.VerifyNoOtherCalls(); + mockDestination.Verify(b => b.UploadRangeFromUriAsync( + sourceResource.Object.Uri, + new HttpRange(0, length), + new HttpRange(0, length), + It.IsAny(), + It.IsAny()), + Times.Once()); + mockDestination.VerifyNoOtherCalls(); + } + + [Test] + public async Task CopyBlockFromUriAsync() + { + // Arrange + int length = 1024; + Mock sourceResource = new(); + sourceResource.Setup(b => b.Uri) + .Returns(new Uri("https://storageaccount.file.core.windows.net/container/sourcefile")); + + Mock mockDestination = new( + new Uri("https://storageaccount.file.core.windows.net/container/destinationfile"), + new ShareClientOptions()); + + mockDestination.Setup(b => b.UploadRangeFromUriAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(Task.FromResult(Response.FromValue( + ShareModelFactory.ShareFileUploadInfo( + eTag: new ETag("eTag"), + lastModified: DateTimeOffset.UtcNow, + contentHash: default, + isServerEncrypted: false), + new MockResponse(200)))); + ShareFileStorageResource destinationResource = new ShareFileStorageResource(mockDestination.Object); + + // Act + await destinationResource.CopyBlockFromUriInternalAsync( + sourceResource: sourceResource.Object, + overwrite: false, + range: new HttpRange(0, length), + completeLength: length); + + sourceResource.Verify(b => b.Uri, Times.Once()); + sourceResource.VerifyNoOtherCalls(); + mockDestination.Verify(b => b.UploadRangeFromUriAsync( + sourceResource.Object.Uri, + new HttpRange(0, length), + new HttpRange(0, length), + It.IsAny(), + It.IsAny()), + Times.Once()); + mockDestination.VerifyNoOtherCalls(); + } + + [Test] + public async Task CopyBlockFromUriAsync_Error() + { + // Arrange + Mock sourceResource = new(); + sourceResource.Setup(b => b.Uri) + .Returns(new Uri("https://storageaccount.file.core.windows.net/container/sourcefile")); + + Mock mockDestination = new( + new Uri("https://storageaccount.file.core.windows.net/container/destinationfile"), + new ShareClientOptions()); + + mockDestination.Setup(b => b.UploadRangeFromUriAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Throws(new RequestFailedException(status: 404, message: "The specified resource does not exist.", errorCode: "ResourceNotFound", default)); + ShareFileStorageResource destinationResource = new ShareFileStorageResource(mockDestination.Object); + + // Act + int length = 1024; + await TestHelper.AssertExpectedExceptionAsync( + destinationResource.CopyBlockFromUriInternalAsync(sourceResource.Object, new HttpRange(0, length), false, length), + e => + { + Assert.AreEqual("ResourceNotFound", e.ErrorCode); + }); + + sourceResource.Verify(b => b.Uri, Times.Once()); + sourceResource.VerifyNoOtherCalls(); + mockDestination.Verify(b => b.UploadRangeFromUriAsync( + sourceResource.Object.Uri, + new HttpRange(0, length), + new HttpRange(0, length), + It.IsAny(), + It.IsAny()), + Times.Once()); + mockDestination.VerifyNoOtherCalls(); + } + + [Test] + public async Task GetPropertiesAsync() + { + // Arrange + Mock mock = new( + new Uri("https://storageaccount.file.core.windows.net/container/file"), + new ShareClientOptions()); + + long length = 1024; + string source = "https://storageaccount.file.core.windows.net/container/file2"; + mock.Setup(b => b.GetPropertiesAsync(It.IsAny(), It.IsAny())) + .Returns(Task.FromResult(Response.FromValue( + FilesModelFactory.StorageFileProperties( + lastModified: DateTime.MinValue, + metadata: default, + contentLength: length, + contentType: default, + eTag: new ETag("etag"), + contentHash: default, + contentEncoding: default, + cacheControl: default, + contentDisposition: default, + contentLanguage: default, + copyCompletedOn: DateTimeOffset.MinValue, + copyStatusDescription: default, + copyId: default, + copyProgress: default, + copySource: source, + copyStatus: CopyStatus.Success, + isServerEncrypted: false, + fileAttributes: default, + fileCreationTime: DateTimeOffset.MinValue, + fileLastWriteTime: DateTimeOffset.MinValue, + fileChangeTime: DateTimeOffset.MinValue, + filePermissionKey: default, + fileId: default, + fileParentId: default), + new MockResponse(200)))); + + ShareFileStorageResource storageResource = new ShareFileStorageResource(mock.Object); + + // Act + StorageResourceProperties result = await storageResource.GetPropertiesInternalAsync(); + Mock properties = new Mock(result); + + // Assert + Assert.NotNull(result); + mock.Verify(b => b.GetPropertiesAsync(It.IsAny(), It.IsAny()), + Times.Once()); + mock.VerifyNoOtherCalls(); + } + + [Test] + public async Task GetPropertiesAsync_Error() + { + // Arrange + Mock mock = new( + new Uri("https://storageaccount.file.core.windows.net/container/file"), + new ShareClientOptions()); + + mock.Setup(b => b.GetPropertiesAsync(It.IsAny(), It.IsAny())) + .Throws(new RequestFailedException(status: 404, message: "The specified resource does not exist.", errorCode: "ResourceNotFound", default)); + + ShareFileStorageResource storageResource = new ShareFileStorageResource(mock.Object); + + // Act without creating the blob + await TestHelper.AssertExpectedExceptionAsync( + storageResource.GetPropertiesInternalAsync(), + e => + { + Assert.AreEqual("ResourceNotFound", e.ErrorCode); + }); + mock.Verify(b => b.GetPropertiesAsync(It.IsAny(), It.IsAny()), + Times.Once()); + mock.VerifyNoOtherCalls(); + } + } +} diff --git a/sdk/storage/Azure.Storage.DataMovement/api/Azure.Storage.DataMovement.net6.0.cs b/sdk/storage/Azure.Storage.DataMovement/api/Azure.Storage.DataMovement.net6.0.cs index febae2eec9d73..ee62763d45fad 100644 --- a/sdk/storage/Azure.Storage.DataMovement/api/Azure.Storage.DataMovement.net6.0.cs +++ b/sdk/storage/Azure.Storage.DataMovement/api/Azure.Storage.DataMovement.net6.0.cs @@ -178,8 +178,8 @@ public StorageResourceReadStreamResult(System.IO.Stream content, string contentR public partial class StorageResourceWriteToOffsetOptions { public StorageResourceWriteToOffsetOptions() { } - public string BlockId { get { throw null; } } - public long? Position { get { throw null; } } + public string BlockId { get { throw null; } set { } } + public long? Position { get { throw null; } set { } } } public partial class TransferCheckpointStoreOptions { diff --git a/sdk/storage/Azure.Storage.DataMovement/api/Azure.Storage.DataMovement.netstandard2.0.cs b/sdk/storage/Azure.Storage.DataMovement/api/Azure.Storage.DataMovement.netstandard2.0.cs index febae2eec9d73..ee62763d45fad 100644 --- a/sdk/storage/Azure.Storage.DataMovement/api/Azure.Storage.DataMovement.netstandard2.0.cs +++ b/sdk/storage/Azure.Storage.DataMovement/api/Azure.Storage.DataMovement.netstandard2.0.cs @@ -178,8 +178,8 @@ public StorageResourceReadStreamResult(System.IO.Stream content, string contentR public partial class StorageResourceWriteToOffsetOptions { public StorageResourceWriteToOffsetOptions() { } - public string BlockId { get { throw null; } } - public long? Position { get { throw null; } } + public string BlockId { get { throw null; } set { } } + public long? Position { get { throw null; } set { } } } public partial class TransferCheckpointStoreOptions { diff --git a/sdk/storage/Azure.Storage.DataMovement/src/Shared/StorageResourceItemInternal.cs b/sdk/storage/Azure.Storage.DataMovement/src/Shared/StorageResourceItemInternal.cs new file mode 100644 index 0000000000000..9314bdd4e43a9 --- /dev/null +++ b/sdk/storage/Azure.Storage.DataMovement/src/Shared/StorageResourceItemInternal.cs @@ -0,0 +1,76 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace Azure.Storage.DataMovement +{ + /// + /// This is used internally for testing purposes. It is shared within the test packages. + /// + internal abstract class StorageResourceItemInternal : StorageResourceItem + { + internal Task CompleteTransferInternalAsync(bool overwrite, CancellationToken cancellationToken = default) + => CompleteTransferAsync(overwrite, cancellationToken); + + internal Task CopyBlockFromUriInternalAsync( + StorageResourceItem sourceResource, + HttpRange range, + bool overwrite, + long completeLength, + StorageResourceCopyFromUriOptions options = null, + CancellationToken cancellationToken = default) + => CopyBlockFromUriAsync( + sourceResource, + range, + overwrite, + completeLength, + options, + cancellationToken); + + internal Task CopyFromStreamInternalAsync( + Stream stream, + long streamLength, + bool overwrite, + long completeLength, + StorageResourceWriteToOffsetOptions options = null, + CancellationToken cancellationToken = default) + => CopyFromStreamAsync( + stream, + streamLength, + overwrite, + completeLength, + options, + cancellationToken); + + internal Task CopyFromUriInternalAsync( + StorageResourceItem sourceResource, + bool overwrite, + long completeLength, + StorageResourceCopyFromUriOptions options = null, + CancellationToken cancellationToken = default) + => CopyFromUriAsync( + sourceResource, + overwrite, + completeLength, + options, + cancellationToken); + + internal Task DeleteIfExistsInternalAsync(CancellationToken cancellationToken = default) + => DeleteIfExistsAsync(cancellationToken); + + internal Task GetCopyAuthorizationHeaderInternalAsync(CancellationToken cancellationToken = default) + => GetCopyAuthorizationHeaderAsync(cancellationToken); + + internal Task GetPropertiesInternalAsync(CancellationToken token = default) + => GetPropertiesAsync(token); + + internal Task ReadStreamInternalAsync( + long position = 0, + long? length = null, + CancellationToken cancellationToken = default) + => ReadStreamAsync(position, length, cancellationToken); + } +} diff --git a/sdk/storage/Azure.Storage.DataMovement/src/StorageResourceWriteToOffsetOptions.cs b/sdk/storage/Azure.Storage.DataMovement/src/StorageResourceWriteToOffsetOptions.cs index 3f4d199bb61c7..b4ecdae36a7bd 100644 --- a/sdk/storage/Azure.Storage.DataMovement/src/StorageResourceWriteToOffsetOptions.cs +++ b/sdk/storage/Azure.Storage.DataMovement/src/StorageResourceWriteToOffsetOptions.cs @@ -17,11 +17,11 @@ public class StorageResourceWriteToOffsetOptions /// /// Applies only to block blobs. /// - public string BlockId { get; internal set; } + public string BlockId { get; set; } /// /// Optional. Specifies the position to write to. Will default to 0 if not specified. /// - public long? Position { get; internal set; } + public long? Position { get; set; } } }