diff --git a/sdk/storage/Azure.Storage.Blobs.Batch/CHANGELOG.md b/sdk/storage/Azure.Storage.Blobs.Batch/CHANGELOG.md index 5952194b2d2ea..64c9a4b2a297d 100644 --- a/sdk/storage/Azure.Storage.Blobs.Batch/CHANGELOG.md +++ b/sdk/storage/Azure.Storage.Blobs.Batch/CHANGELOG.md @@ -3,12 +3,7 @@ ## 12.20.0-beta.1 (Unreleased) ### Features Added - -### Breaking Changes - -### Bugs Fixed - -### Other Changes +- Added support for service version 2025-01-05. ## 12.19.0 (2024-09-18) diff --git a/sdk/storage/Azure.Storage.Blobs.ChangeFeed/CHANGELOG.md b/sdk/storage/Azure.Storage.Blobs.ChangeFeed/CHANGELOG.md index 8e03ceb02b5fe..6fccde404a9b0 100644 --- a/sdk/storage/Azure.Storage.Blobs.ChangeFeed/CHANGELOG.md +++ b/sdk/storage/Azure.Storage.Blobs.ChangeFeed/CHANGELOG.md @@ -3,12 +3,7 @@ ## 12.0.0-preview.50 (Unreleased) ### Features Added - -### Breaking Changes - -### Bugs Fixed - -### Other Changes +- Added support for service version 2025-01-05. ## 12.0.0-preview.49 (2024-09-18) diff --git a/sdk/storage/Azure.Storage.Blobs.ChangeFeed/tests/ChangeFeedTestBase.cs b/sdk/storage/Azure.Storage.Blobs.ChangeFeed/tests/ChangeFeedTestBase.cs index cc5c65ae20673..d65ba1264ce66 100644 --- a/sdk/storage/Azure.Storage.Blobs.ChangeFeed/tests/ChangeFeedTestBase.cs +++ b/sdk/storage/Azure.Storage.Blobs.ChangeFeed/tests/ChangeFeedTestBase.cs @@ -32,6 +32,7 @@ namespace Azure.Storage.Blobs.ChangeFeed.Tests BlobClientOptions.ServiceVersion.V2024_05_04, BlobClientOptions.ServiceVersion.V2024_08_04, BlobClientOptions.ServiceVersion.V2024_11_04, + BlobClientOptions.ServiceVersion.V2025_01_05, StorageVersionExtensions.LatestVersion, StorageVersionExtensions.MaxVersion, RecordingServiceVersion = StorageVersionExtensions.MaxVersion, diff --git a/sdk/storage/Azure.Storage.Blobs/AzureStorageNetMigrationV12.md b/sdk/storage/Azure.Storage.Blobs/AzureStorageNetMigrationV12.md index 68f523f715716..8faad4f14059e 100644 --- a/sdk/storage/Azure.Storage.Blobs/AzureStorageNetMigrationV12.md +++ b/sdk/storage/Azure.Storage.Blobs/AzureStorageNetMigrationV12.md @@ -611,6 +611,54 @@ BlobSasBuilder sasBuilder = new BlobSasBuilder }; ``` +To create a simple User Delegation SAS with any optional parameters, use the convenience overload of GenerateUserDelegationSas which only requires taking in permissions and the expiry time. + +```C# Snippet:SampleSnippetsBlobMigration_GenerateUserDelegationSas +// Create a BlobClient +BlobClient blobClient = new BlobClient(blobUri); + +// Create full, self-authenticating URI to the resource from the BlobClient +Uri sasUri = blobClient.GenerateUserDelegationSasUri(BlobSasPermissions.Read, DateTimeOffset.UtcNow.AddHours(1), userDelegationKey); +``` + +To create a more complex User Delegation SAS, pass the SAS builder to the GenerateUserDelegationSas method. + +```C# Snippet:SampleSnippetsBlobMigration_GenerateUserDelegationSas_Builder +// Create a BlobClient +BlobClient blobClient = new BlobClient(blobUri); +// Create BlobSasBuilder and specify parameters +BlobSasBuilder sasBuilder = new BlobSasBuilder(BlobSasPermissions.Read, DateTimeOffset.UtcNow.AddHours(1)) +{ + // Since we are generating from the client, the client will have the container and blob name + // Specify any optional paremeters here + StartsOn = DateTimeOffset.UtcNow.AddHours(-1) +}; + +// Create full, self-authenticating URI to the resource from the BlobClient +Uri sasUri = blobClient.GenerateUserDelegationSasUri(sasBuilder, userDelegationKey); +``` + +You can also generate an User Delegation SAS without use of the client. + +```C# Snippet:SampleSnippetsBlobMigration_UserDelegationSasBuilder +// Create BlobSasBuilder and specify parameters +BlobSasBuilder sasBuilder = new BlobSasBuilder(BlobSasPermissions.Read, DateTimeOffset.UtcNow.AddHours(1)) +{ + // with no url in a client to read from, container and blob name must be provided if applicable + BlobContainerName = containerName, + BlobName = blobName +}; + +// Create full, self-authenticating URI to the resource +BlobUriBuilder uriBuilder = new BlobUriBuilder(StorageAccountBlobUri) +{ + BlobContainerName = containerName, + BlobName = blobName, + Sas = sasBuilder.ToSasQueryParameters(userDelegationKey, accountName) +}; +Uri sasUri = uriBuilder.ToUri(); +``` + ### Content Hashes #### Blob Content MD5 diff --git a/sdk/storage/Azure.Storage.Blobs/CHANGELOG.md b/sdk/storage/Azure.Storage.Blobs/CHANGELOG.md index 7fb652e464e4c..2450bdef7e6cc 100644 --- a/sdk/storage/Azure.Storage.Blobs/CHANGELOG.md +++ b/sdk/storage/Azure.Storage.Blobs/CHANGELOG.md @@ -3,14 +3,13 @@ ## 12.23.0-beta.1 (Unreleased) ### Features Added - -### Breaking Changes +- Added support for service version 2025-01-05. +- Added GenerateUserDelegationSasUri() to BlobBaseClient and BlobContainerClient. +- Added BlobErrorCode.BlobAccessTierNotSupportedForAccountType enum value. ### Bugs Fixed - Fixed bug where BlobClient.Upload(BinaryData content, ..) did not properly dispose stream after wrapping the BinaryData passed. -### Other Changes - ## 12.22.1 (2024-09-25) ### Other Changes diff --git a/sdk/storage/Azure.Storage.Blobs/api/Azure.Storage.Blobs.net6.0.cs b/sdk/storage/Azure.Storage.Blobs/api/Azure.Storage.Blobs.net6.0.cs index 22a57254be04d..d93de39ce28c0 100644 --- a/sdk/storage/Azure.Storage.Blobs/api/Azure.Storage.Blobs.net6.0.cs +++ b/sdk/storage/Azure.Storage.Blobs/api/Azure.Storage.Blobs.net6.0.cs @@ -51,7 +51,7 @@ public BlobClient(System.Uri blobUri, Azure.Storage.StorageSharedKeyCredential c } public partial class BlobClientOptions : Azure.Core.ClientOptions { - public BlobClientOptions(Azure.Storage.Blobs.BlobClientOptions.ServiceVersion version = Azure.Storage.Blobs.BlobClientOptions.ServiceVersion.V2024_11_04) { } + public BlobClientOptions(Azure.Storage.Blobs.BlobClientOptions.ServiceVersion version = Azure.Storage.Blobs.BlobClientOptions.ServiceVersion.V2025_01_05) { } public Azure.Storage.Blobs.Models.BlobAudience? Audience { get { throw null; } set { } } public Azure.Storage.Blobs.Models.CustomerProvidedKey? CustomerProvidedKey { get { throw null; } set { } } public bool EnableTenantDiscovery { get { throw null; } set { } } @@ -87,6 +87,7 @@ public enum ServiceVersion V2024_05_04 = 22, V2024_08_04 = 23, V2024_11_04 = 24, + V2025_01_05 = 25, } } public partial class BlobContainerClient @@ -136,6 +137,12 @@ public BlobContainerClient(System.Uri blobContainerUri, Azure.Storage.StorageSha public virtual System.Uri GenerateSasUri(Azure.Storage.Sas.BlobSasBuilder builder) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public virtual System.Uri GenerateSasUri(Azure.Storage.Sas.BlobSasBuilder builder, out string stringToSign) { throw null; } + public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.BlobContainerSasPermissions permissions, System.DateTimeOffset expiresOn, Azure.Storage.Blobs.Models.UserDelegationKey userDelegationKey) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.BlobContainerSasPermissions permissions, System.DateTimeOffset expiresOn, Azure.Storage.Blobs.Models.UserDelegationKey userDelegationKey, out string stringToSign) { throw null; } + public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.BlobSasBuilder builder, Azure.Storage.Blobs.Models.UserDelegationKey userDelegationKey) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.BlobSasBuilder builder, Azure.Storage.Blobs.Models.UserDelegationKey userDelegationKey, out string stringToSign) { throw null; } public virtual Azure.Response GetAccessPolicy(Azure.Storage.Blobs.Models.BlobRequestConditions conditions = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task> GetAccessPolicyAsync(Azure.Storage.Blobs.Models.BlobRequestConditions conditions = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual Azure.Response GetAccountInfo(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } @@ -608,6 +615,7 @@ public BlobDownloadToOptions() { } public static Azure.Storage.Blobs.Models.BlobErrorCode AuthorizationResourceTypeMismatch { get { throw null; } } public static Azure.Storage.Blobs.Models.BlobErrorCode AuthorizationServiceMismatch { get { throw null; } } public static Azure.Storage.Blobs.Models.BlobErrorCode AuthorizationSourceIPMismatch { get { throw null; } } + public static Azure.Storage.Blobs.Models.BlobErrorCode BlobAccessTierNotSupportedForAccountType { get { throw null; } } public static Azure.Storage.Blobs.Models.BlobErrorCode BlobAlreadyExists { get { throw null; } } public static Azure.Storage.Blobs.Models.BlobErrorCode BlobArchived { get { throw null; } } public static Azure.Storage.Blobs.Models.BlobErrorCode BlobBeingRehydrated { get { throw null; } } @@ -1640,6 +1648,12 @@ public BlobBaseClient(System.Uri blobUri, Azure.Storage.StorageSharedKeyCredenti public virtual System.Uri GenerateSasUri(Azure.Storage.Sas.BlobSasPermissions permissions, System.DateTimeOffset expiresOn) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public virtual System.Uri GenerateSasUri(Azure.Storage.Sas.BlobSasPermissions permissions, System.DateTimeOffset expiresOn, out string stringToSign) { throw null; } + public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.BlobSasBuilder builder, Azure.Storage.Blobs.Models.UserDelegationKey userDelegationKey) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.BlobSasBuilder builder, Azure.Storage.Blobs.Models.UserDelegationKey userDelegationKey, out string stringToSign) { throw null; } + public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.BlobSasPermissions permissions, System.DateTimeOffset expiresOn, Azure.Storage.Blobs.Models.UserDelegationKey userDelegationKey) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.BlobSasPermissions permissions, System.DateTimeOffset expiresOn, Azure.Storage.Blobs.Models.UserDelegationKey userDelegationKey, out string stringToSign) { throw null; } public virtual Azure.Response GetAccountInfo(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task> GetAccountInfoAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } protected internal virtual Azure.Storage.Blobs.Specialized.BlobLeaseClient GetBlobLeaseClientCore(string leaseId) { throw null; } @@ -1836,7 +1850,7 @@ public PageBlobClient(System.Uri blobUri, Azure.Storage.StorageSharedKeyCredenti } public partial class SpecializedBlobClientOptions : Azure.Storage.Blobs.BlobClientOptions { - public SpecializedBlobClientOptions(Azure.Storage.Blobs.BlobClientOptions.ServiceVersion version = Azure.Storage.Blobs.BlobClientOptions.ServiceVersion.V2024_11_04) : base (default(Azure.Storage.Blobs.BlobClientOptions.ServiceVersion)) { } + public SpecializedBlobClientOptions(Azure.Storage.Blobs.BlobClientOptions.ServiceVersion version = Azure.Storage.Blobs.BlobClientOptions.ServiceVersion.V2025_01_05) : base (default(Azure.Storage.Blobs.BlobClientOptions.ServiceVersion)) { } public Azure.Storage.ClientSideEncryptionOptions ClientSideEncryption { get { throw null; } set { } } } public static partial class SpecializedBlobExtensions diff --git a/sdk/storage/Azure.Storage.Blobs/api/Azure.Storage.Blobs.netstandard2.0.cs b/sdk/storage/Azure.Storage.Blobs/api/Azure.Storage.Blobs.netstandard2.0.cs index 22a57254be04d..d93de39ce28c0 100644 --- a/sdk/storage/Azure.Storage.Blobs/api/Azure.Storage.Blobs.netstandard2.0.cs +++ b/sdk/storage/Azure.Storage.Blobs/api/Azure.Storage.Blobs.netstandard2.0.cs @@ -51,7 +51,7 @@ public BlobClient(System.Uri blobUri, Azure.Storage.StorageSharedKeyCredential c } public partial class BlobClientOptions : Azure.Core.ClientOptions { - public BlobClientOptions(Azure.Storage.Blobs.BlobClientOptions.ServiceVersion version = Azure.Storage.Blobs.BlobClientOptions.ServiceVersion.V2024_11_04) { } + public BlobClientOptions(Azure.Storage.Blobs.BlobClientOptions.ServiceVersion version = Azure.Storage.Blobs.BlobClientOptions.ServiceVersion.V2025_01_05) { } public Azure.Storage.Blobs.Models.BlobAudience? Audience { get { throw null; } set { } } public Azure.Storage.Blobs.Models.CustomerProvidedKey? CustomerProvidedKey { get { throw null; } set { } } public bool EnableTenantDiscovery { get { throw null; } set { } } @@ -87,6 +87,7 @@ public enum ServiceVersion V2024_05_04 = 22, V2024_08_04 = 23, V2024_11_04 = 24, + V2025_01_05 = 25, } } public partial class BlobContainerClient @@ -136,6 +137,12 @@ public BlobContainerClient(System.Uri blobContainerUri, Azure.Storage.StorageSha public virtual System.Uri GenerateSasUri(Azure.Storage.Sas.BlobSasBuilder builder) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public virtual System.Uri GenerateSasUri(Azure.Storage.Sas.BlobSasBuilder builder, out string stringToSign) { throw null; } + public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.BlobContainerSasPermissions permissions, System.DateTimeOffset expiresOn, Azure.Storage.Blobs.Models.UserDelegationKey userDelegationKey) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.BlobContainerSasPermissions permissions, System.DateTimeOffset expiresOn, Azure.Storage.Blobs.Models.UserDelegationKey userDelegationKey, out string stringToSign) { throw null; } + public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.BlobSasBuilder builder, Azure.Storage.Blobs.Models.UserDelegationKey userDelegationKey) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.BlobSasBuilder builder, Azure.Storage.Blobs.Models.UserDelegationKey userDelegationKey, out string stringToSign) { throw null; } public virtual Azure.Response GetAccessPolicy(Azure.Storage.Blobs.Models.BlobRequestConditions conditions = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task> GetAccessPolicyAsync(Azure.Storage.Blobs.Models.BlobRequestConditions conditions = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual Azure.Response GetAccountInfo(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } @@ -608,6 +615,7 @@ public BlobDownloadToOptions() { } public static Azure.Storage.Blobs.Models.BlobErrorCode AuthorizationResourceTypeMismatch { get { throw null; } } public static Azure.Storage.Blobs.Models.BlobErrorCode AuthorizationServiceMismatch { get { throw null; } } public static Azure.Storage.Blobs.Models.BlobErrorCode AuthorizationSourceIPMismatch { get { throw null; } } + public static Azure.Storage.Blobs.Models.BlobErrorCode BlobAccessTierNotSupportedForAccountType { get { throw null; } } public static Azure.Storage.Blobs.Models.BlobErrorCode BlobAlreadyExists { get { throw null; } } public static Azure.Storage.Blobs.Models.BlobErrorCode BlobArchived { get { throw null; } } public static Azure.Storage.Blobs.Models.BlobErrorCode BlobBeingRehydrated { get { throw null; } } @@ -1640,6 +1648,12 @@ public BlobBaseClient(System.Uri blobUri, Azure.Storage.StorageSharedKeyCredenti public virtual System.Uri GenerateSasUri(Azure.Storage.Sas.BlobSasPermissions permissions, System.DateTimeOffset expiresOn) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public virtual System.Uri GenerateSasUri(Azure.Storage.Sas.BlobSasPermissions permissions, System.DateTimeOffset expiresOn, out string stringToSign) { throw null; } + public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.BlobSasBuilder builder, Azure.Storage.Blobs.Models.UserDelegationKey userDelegationKey) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.BlobSasBuilder builder, Azure.Storage.Blobs.Models.UserDelegationKey userDelegationKey, out string stringToSign) { throw null; } + public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.BlobSasPermissions permissions, System.DateTimeOffset expiresOn, Azure.Storage.Blobs.Models.UserDelegationKey userDelegationKey) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.BlobSasPermissions permissions, System.DateTimeOffset expiresOn, Azure.Storage.Blobs.Models.UserDelegationKey userDelegationKey, out string stringToSign) { throw null; } public virtual Azure.Response GetAccountInfo(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task> GetAccountInfoAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } protected internal virtual Azure.Storage.Blobs.Specialized.BlobLeaseClient GetBlobLeaseClientCore(string leaseId) { throw null; } @@ -1836,7 +1850,7 @@ public PageBlobClient(System.Uri blobUri, Azure.Storage.StorageSharedKeyCredenti } public partial class SpecializedBlobClientOptions : Azure.Storage.Blobs.BlobClientOptions { - public SpecializedBlobClientOptions(Azure.Storage.Blobs.BlobClientOptions.ServiceVersion version = Azure.Storage.Blobs.BlobClientOptions.ServiceVersion.V2024_11_04) : base (default(Azure.Storage.Blobs.BlobClientOptions.ServiceVersion)) { } + public SpecializedBlobClientOptions(Azure.Storage.Blobs.BlobClientOptions.ServiceVersion version = Azure.Storage.Blobs.BlobClientOptions.ServiceVersion.V2025_01_05) : base (default(Azure.Storage.Blobs.BlobClientOptions.ServiceVersion)) { } public Azure.Storage.ClientSideEncryptionOptions ClientSideEncryption { get { throw null; } set { } } } public static partial class SpecializedBlobExtensions diff --git a/sdk/storage/Azure.Storage.Blobs/api/Azure.Storage.Blobs.netstandard2.1.cs b/sdk/storage/Azure.Storage.Blobs/api/Azure.Storage.Blobs.netstandard2.1.cs index 22a57254be04d..d93de39ce28c0 100644 --- a/sdk/storage/Azure.Storage.Blobs/api/Azure.Storage.Blobs.netstandard2.1.cs +++ b/sdk/storage/Azure.Storage.Blobs/api/Azure.Storage.Blobs.netstandard2.1.cs @@ -51,7 +51,7 @@ public BlobClient(System.Uri blobUri, Azure.Storage.StorageSharedKeyCredential c } public partial class BlobClientOptions : Azure.Core.ClientOptions { - public BlobClientOptions(Azure.Storage.Blobs.BlobClientOptions.ServiceVersion version = Azure.Storage.Blobs.BlobClientOptions.ServiceVersion.V2024_11_04) { } + public BlobClientOptions(Azure.Storage.Blobs.BlobClientOptions.ServiceVersion version = Azure.Storage.Blobs.BlobClientOptions.ServiceVersion.V2025_01_05) { } public Azure.Storage.Blobs.Models.BlobAudience? Audience { get { throw null; } set { } } public Azure.Storage.Blobs.Models.CustomerProvidedKey? CustomerProvidedKey { get { throw null; } set { } } public bool EnableTenantDiscovery { get { throw null; } set { } } @@ -87,6 +87,7 @@ public enum ServiceVersion V2024_05_04 = 22, V2024_08_04 = 23, V2024_11_04 = 24, + V2025_01_05 = 25, } } public partial class BlobContainerClient @@ -136,6 +137,12 @@ public BlobContainerClient(System.Uri blobContainerUri, Azure.Storage.StorageSha public virtual System.Uri GenerateSasUri(Azure.Storage.Sas.BlobSasBuilder builder) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public virtual System.Uri GenerateSasUri(Azure.Storage.Sas.BlobSasBuilder builder, out string stringToSign) { throw null; } + public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.BlobContainerSasPermissions permissions, System.DateTimeOffset expiresOn, Azure.Storage.Blobs.Models.UserDelegationKey userDelegationKey) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.BlobContainerSasPermissions permissions, System.DateTimeOffset expiresOn, Azure.Storage.Blobs.Models.UserDelegationKey userDelegationKey, out string stringToSign) { throw null; } + public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.BlobSasBuilder builder, Azure.Storage.Blobs.Models.UserDelegationKey userDelegationKey) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.BlobSasBuilder builder, Azure.Storage.Blobs.Models.UserDelegationKey userDelegationKey, out string stringToSign) { throw null; } public virtual Azure.Response GetAccessPolicy(Azure.Storage.Blobs.Models.BlobRequestConditions conditions = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task> GetAccessPolicyAsync(Azure.Storage.Blobs.Models.BlobRequestConditions conditions = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual Azure.Response GetAccountInfo(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } @@ -608,6 +615,7 @@ public BlobDownloadToOptions() { } public static Azure.Storage.Blobs.Models.BlobErrorCode AuthorizationResourceTypeMismatch { get { throw null; } } public static Azure.Storage.Blobs.Models.BlobErrorCode AuthorizationServiceMismatch { get { throw null; } } public static Azure.Storage.Blobs.Models.BlobErrorCode AuthorizationSourceIPMismatch { get { throw null; } } + public static Azure.Storage.Blobs.Models.BlobErrorCode BlobAccessTierNotSupportedForAccountType { get { throw null; } } public static Azure.Storage.Blobs.Models.BlobErrorCode BlobAlreadyExists { get { throw null; } } public static Azure.Storage.Blobs.Models.BlobErrorCode BlobArchived { get { throw null; } } public static Azure.Storage.Blobs.Models.BlobErrorCode BlobBeingRehydrated { get { throw null; } } @@ -1640,6 +1648,12 @@ public BlobBaseClient(System.Uri blobUri, Azure.Storage.StorageSharedKeyCredenti public virtual System.Uri GenerateSasUri(Azure.Storage.Sas.BlobSasPermissions permissions, System.DateTimeOffset expiresOn) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public virtual System.Uri GenerateSasUri(Azure.Storage.Sas.BlobSasPermissions permissions, System.DateTimeOffset expiresOn, out string stringToSign) { throw null; } + public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.BlobSasBuilder builder, Azure.Storage.Blobs.Models.UserDelegationKey userDelegationKey) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.BlobSasBuilder builder, Azure.Storage.Blobs.Models.UserDelegationKey userDelegationKey, out string stringToSign) { throw null; } + public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.BlobSasPermissions permissions, System.DateTimeOffset expiresOn, Azure.Storage.Blobs.Models.UserDelegationKey userDelegationKey) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.BlobSasPermissions permissions, System.DateTimeOffset expiresOn, Azure.Storage.Blobs.Models.UserDelegationKey userDelegationKey, out string stringToSign) { throw null; } public virtual Azure.Response GetAccountInfo(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task> GetAccountInfoAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } protected internal virtual Azure.Storage.Blobs.Specialized.BlobLeaseClient GetBlobLeaseClientCore(string leaseId) { throw null; } @@ -1836,7 +1850,7 @@ public PageBlobClient(System.Uri blobUri, Azure.Storage.StorageSharedKeyCredenti } public partial class SpecializedBlobClientOptions : Azure.Storage.Blobs.BlobClientOptions { - public SpecializedBlobClientOptions(Azure.Storage.Blobs.BlobClientOptions.ServiceVersion version = Azure.Storage.Blobs.BlobClientOptions.ServiceVersion.V2024_11_04) : base (default(Azure.Storage.Blobs.BlobClientOptions.ServiceVersion)) { } + public SpecializedBlobClientOptions(Azure.Storage.Blobs.BlobClientOptions.ServiceVersion version = Azure.Storage.Blobs.BlobClientOptions.ServiceVersion.V2025_01_05) : base (default(Azure.Storage.Blobs.BlobClientOptions.ServiceVersion)) { } public Azure.Storage.ClientSideEncryptionOptions ClientSideEncryption { get { throw null; } set { } } } public static partial class SpecializedBlobExtensions diff --git a/sdk/storage/Azure.Storage.Blobs/assets.json b/sdk/storage/Azure.Storage.Blobs/assets.json index 19f657cf818c5..0facb33e2a026 100644 --- a/sdk/storage/Azure.Storage.Blobs/assets.json +++ b/sdk/storage/Azure.Storage.Blobs/assets.json @@ -2,5 +2,5 @@ "AssetsRepo": "Azure/azure-sdk-assets", "AssetsRepoPrefixPath": "net", "TagPrefix": "net/storage/Azure.Storage.Blobs", - "Tag": "net/storage/Azure.Storage.Blobs_a4e8ca8040" + "Tag": "net/storage/Azure.Storage.Blobs_5c382dfb14" } diff --git a/sdk/storage/Azure.Storage.Blobs/samples/Sample03_Migrations.cs b/sdk/storage/Azure.Storage.Blobs/samples/Sample03_Migrations.cs index d1f81e4c461e0..198c3ba8f9731 100644 --- a/sdk/storage/Azure.Storage.Blobs/samples/Sample03_Migrations.cs +++ b/sdk/storage/Azure.Storage.Blobs/samples/Sample03_Migrations.cs @@ -859,6 +859,139 @@ public async Task SasBuilderIdentifier() } } + [Test] + public async Task UserDelegationSasBuilder() + { + string accountName = StorageAccountName; + string containerName = Randomize("sample-container"); + string blobName = Randomize("sample-blob"); + BlobServiceClient client = new BlobServiceClient(ConnectionString); + Response userDelegationKeyResponse = await client.GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: DateTimeOffset.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + + // setup blob + BlobContainerClient container = new BlobContainerClient(ConnectionString, containerName); + + try + { + await container.CreateAsync(); + await container.GetBlobClient(blobName).UploadAsync(BinaryData.FromString("hello world")); + + #region Snippet:SampleSnippetsBlobMigration_UserDelegationSasBuilder + // Create BlobSasBuilder and specify parameters + BlobSasBuilder sasBuilder = new BlobSasBuilder(BlobSasPermissions.Read, DateTimeOffset.UtcNow.AddHours(1)) + { + // with no url in a client to read from, container and blob name must be provided if applicable + BlobContainerName = containerName, + BlobName = blobName + }; + + // Create full, self-authenticating URI to the resource + BlobUriBuilder uriBuilder = new BlobUriBuilder(StorageAccountBlobUri) + { + BlobContainerName = containerName, + BlobName = blobName, + Sas = sasBuilder.ToSasQueryParameters(userDelegationKey, accountName) + }; + Uri sasUri = uriBuilder.ToUri(); + #endregion + + // successful download indicates pass + await new BlobClient(sasUri).DownloadToAsync(new MemoryStream()); + } + finally + { + await container.DeleteIfExistsAsync(); + } + } + + [Test] + public async Task GenerateUserDelegationSas() + { + string accountName = StorageAccountName; + string containerName = Randomize("sample-container"); + string blobName = Randomize("sample-blob"); + BlobServiceClient client = new BlobServiceClient(ConnectionString); + Response userDelegationKeyResponse = await client.GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: DateTimeOffset.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + + // setup blob + BlobContainerClient container = new BlobContainerClient(ConnectionString, containerName); + BlobUriBuilder uriBuilder = new BlobUriBuilder(container.Uri) { BlobName = blobName }; + Uri blobUri = uriBuilder.ToUri(); + + try + { + await container.CreateAsync(); + await container.GetBlobClient(blobName).UploadAsync(BinaryData.FromString("hello world")); + + #region Snippet:SampleSnippetsBlobMigration_GenerateUserDelegationSas + // Create a BlobClient + BlobClient blobClient = new BlobClient(blobUri); + + // Create full, self-authenticating URI to the resource from the BlobClient + Uri sasUri = blobClient.GenerateUserDelegationSasUri(BlobSasPermissions.Read, DateTimeOffset.UtcNow.AddHours(1), userDelegationKey); + #endregion + + // Use newly made SAS URI to download the blob + await new BlobClient(sasUri).DownloadToAsync(new MemoryStream()); + } + finally + { + await container.DeleteIfExistsAsync(); + } + } + + [Test] + public async Task GenerateUserDelegationSas_Builder() + { + string accountName = StorageAccountName; + string containerName = Randomize("sample-container"); + string blobName = Randomize("sample-blob"); + BlobServiceClient client = new BlobServiceClient(ConnectionString); + Response userDelegationKeyResponse = await client.GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: DateTimeOffset.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + + // setup blob + BlobContainerClient container = new BlobContainerClient(ConnectionString, containerName); + BlobUriBuilder uriBuilder = new BlobUriBuilder(container.Uri) { BlobName = blobName }; + Uri blobUri = uriBuilder.ToUri(); + + try + { + await container.CreateAsync(); + await container.GetBlobClient(blobName).UploadAsync(BinaryData.FromString("hello world")); + + #region Snippet:SampleSnippetsBlobMigration_GenerateUserDelegationSas_Builder + // Create a BlobClient + BlobClient blobClient = new BlobClient(blobUri); + // Create BlobSasBuilder and specify parameters + BlobSasBuilder sasBuilder = new BlobSasBuilder(BlobSasPermissions.Read, DateTimeOffset.UtcNow.AddHours(1)) + { + // Since we are generating from the client, the client will have the container and blob name + // Specify any optional paremeters here + StartsOn = DateTimeOffset.UtcNow.AddHours(-1) + }; + + // Create full, self-authenticating URI to the resource from the BlobClient + Uri sasUri = blobClient.GenerateUserDelegationSasUri(sasBuilder, userDelegationKey); + #endregion + + // Use newly made SAS URI to download the blob + await new BlobClient(sasUri).DownloadToAsync(new MemoryStream()); + } + finally + { + await container.DeleteIfExistsAsync(); + } + } + [Test] public async Task BlobContentHash() { diff --git a/sdk/storage/Azure.Storage.Blobs/src/BlobBaseClient.cs b/sdk/storage/Azure.Storage.Blobs/src/BlobBaseClient.cs index 40bdafaf1dd08..aa91edb9f6c41 100644 --- a/sdk/storage/Azure.Storage.Blobs/src/BlobBaseClient.cs +++ b/sdk/storage/Azure.Storage.Blobs/src/BlobBaseClient.cs @@ -6774,42 +6774,167 @@ public virtual Uri GenerateSasUri(BlobSasBuilder builder, out string stringToSig // Deep copy of builder so we don't modify the user's original BlobSasBuilder. builder = BlobSasBuilder.DeepCopy(builder); - // Assign builder's ContainerName, BlobName, Snapshot, BlobVersionId, and EncryptionScope if they are null. - builder.BlobContainerName ??= BlobContainerName; - builder.BlobName ??= Name; - builder.Snapshot ??= _snapshot; - builder.BlobVersionId ??= _blobVersionId; - builder.EncryptionScope ??= _clientConfiguration.EncryptionScope; - - if (!builder.BlobContainerName.Equals(BlobContainerName, StringComparison.InvariantCulture)) - { - throw Errors.SasNamesNotMatching( - nameof(builder.BlobContainerName), - nameof(BlobSasBuilder), - nameof(BlobContainerName)); - } - if (!builder.BlobName.Equals(Name, StringComparison.InvariantCulture)) + SetBuilderAndValidate(builder); + BlobUriBuilder sasUri = new BlobUriBuilder(Uri, ClientConfiguration.TrimBlobNameSlashes) { - throw Errors.SasNamesNotMatching( - nameof(builder.BlobName), - nameof(BlobSasBuilder), - nameof(Name)); - } - if (string.Compare(_snapshot, builder.Snapshot, StringComparison.InvariantCulture) != 0) + Sas = builder.ToSasQueryParameters(ClientConfiguration.SharedKeyCredential, out stringToSign) + }; + return sasUri.ToUri(); + } + #endregion + + #region GenerateUserDelegationSas + /// + /// The + /// returns a representing a Blob Service + /// Shared Access Signature (SAS) Uri based on the Client properties + /// and parameters passed. The SAS is signed by the user delegation key + /// that is passed in. + /// + /// For more information, see + /// + /// Creating an user delegation SAS. + /// + /// + /// Required. Specifies the list of permissions to be associated with the SAS. + /// See . + /// + /// + /// Required. Specifies the time at which the SAS becomes invalid. This field + /// must be omitted if it has been specified in an associated stored access policy. + /// + /// + /// Required. A returned from + /// . + /// + /// + /// A containing the SAS Uri. + /// + /// + /// A will be thrown if a failure occurs. + /// + [CallerShouldAudit("https://aka.ms/azsdk/callershouldaudit/storage-blobs")] + public virtual Uri GenerateUserDelegationSasUri(BlobSasPermissions permissions, DateTimeOffset expiresOn, UserDelegationKey userDelegationKey) => + GenerateUserDelegationSasUri(permissions, expiresOn, userDelegationKey, out _); + + /// + /// The + /// returns a representing a Blob Service + /// Shared Access Signature (SAS) Uri based on the Client properties + /// and parameters passed. The SAS is signed by the user delegation key + /// that is passed in. + /// + /// For more information, see + /// + /// Creating an user delegation SAS. + /// + /// + /// Required. Specifies the list of permissions to be associated with the SAS. + /// See . + /// + /// + /// Required. Specifies the time at which the SAS becomes invalid. This field + /// must be omitted if it has been specified in an associated stored access policy. + /// + /// + /// Required. A returned from + /// . + /// + /// + /// For debugging purposes only. This string will be overwritten with the string to sign that was used to generate the SAS Uri. + /// + /// + /// A containing the SAS Uri. + /// + /// + /// A will be thrown if a failure occurs. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + [CallerShouldAudit("https://aka.ms/azsdk/callershouldaudit/storage-blobs")] + public virtual Uri GenerateUserDelegationSasUri(BlobSasPermissions permissions, DateTimeOffset expiresOn, UserDelegationKey userDelegationKey, out string stringToSign) => + GenerateUserDelegationSasUri(new BlobSasBuilder(permissions, expiresOn) { - throw Errors.SasNamesNotMatching( - nameof(builder.Snapshot), - nameof(BlobSasBuilder)); - } - if (string.Compare(_blobVersionId, builder.BlobVersionId, StringComparison.InvariantCulture) != 0) + BlobContainerName = BlobContainerName, + BlobName = Name, + Snapshot = _snapshot, + BlobVersionId = _blobVersionId, + EncryptionScope = _clientConfiguration.EncryptionScope + }, userDelegationKey, out stringToSign); + + /// + /// The + /// returns a representing a Blob Service + /// Shared Access Signature (SAS) Uri based on the Client properties + /// and builder passed. The SAS is signed by the user delegation key + /// that is passed in. + /// + /// For more information, see + /// + /// Creating an user delegation SAS. + /// + /// + /// Required. Used to generate a Shared Access Signature (SAS). + /// + /// + /// Required. A returned from + /// . + /// + /// + /// A containing the SAS Uri. + /// + /// + /// A will be thrown if a failure occurs. + /// + [CallerShouldAudit("https://aka.ms/azsdk/callershouldaudit/storage-blobs")] + public virtual Uri GenerateUserDelegationSasUri(BlobSasBuilder builder, UserDelegationKey userDelegationKey) => + GenerateUserDelegationSasUri(builder, userDelegationKey, out _); + + /// + /// The + /// returns a representing a Blob Service + /// Shared Access Signature (SAS) Uri based on the Client properties + /// and builder passed. The SAS is signed by the user delegation key + /// that is passed in. + /// + /// For more information, see + /// + /// Creating an user delegation SAS. + /// + /// + /// Required. Used to generate a Shared Access Signature (SAS). + /// + /// + /// Required. A returned from + /// . + /// + /// + /// For debugging purposes only. This string will be overwritten with the string to sign that was used to generate the SAS Uri. + /// + /// + /// A containing the SAS Uri. + /// + /// + /// A will be thrown if a failure occurs. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + [CallerShouldAudit("https://aka.ms/azsdk/callershouldaudit/storage-blobs")] + public virtual Uri GenerateUserDelegationSasUri(BlobSasBuilder builder, UserDelegationKey userDelegationKey, out string stringToSign) + { + builder = builder ?? throw Errors.ArgumentNull(nameof(builder)); + userDelegationKey = userDelegationKey ?? throw Errors.ArgumentNull(nameof(userDelegationKey)); + + // Deep copy of builder so we don't modify the user's origial BlobSasBuilder. + builder = BlobSasBuilder.DeepCopy(builder); + + SetBuilderAndValidate(builder); + if (string.IsNullOrEmpty(AccountName)) { - throw Errors.SasNamesNotMatching( - nameof(builder.BlobVersionId), - nameof(BlobSasBuilder)); + throw Errors.SasClientMissingData(nameof(AccountName)); } + BlobUriBuilder sasUri = new BlobUriBuilder(Uri, ClientConfiguration.TrimBlobNameSlashes) { - Sas = builder.ToSasQueryParameters(ClientConfiguration.SharedKeyCredential, out stringToSign) + Sas = builder.ToSasQueryParameters(userDelegationKey, AccountName, out stringToSign) }; return sasUri.ToUri(); } @@ -6847,6 +6972,44 @@ protected internal virtual BlobContainerClient GetParentBlobContainerClientCore( return _parentBlobContainerClient; } #endregion + + private void SetBuilderAndValidate(BlobSasBuilder builder) + { + // Assign builder's ContainerName, BlobName, Snapshot, BlobVersionId, and EncryptionScope if they are null. + builder.BlobContainerName ??= BlobContainerName; + builder.BlobName ??= Name; + builder.Snapshot ??= _snapshot; + builder.BlobVersionId ??= _blobVersionId; + builder.EncryptionScope ??= _clientConfiguration.EncryptionScope; + + // Validate that builder is properly set + if (!builder.BlobContainerName.Equals(BlobContainerName, StringComparison.InvariantCulture)) + { + throw Errors.SasNamesNotMatching( + nameof(builder.BlobContainerName), + nameof(BlobSasBuilder), + nameof(BlobContainerName)); + } + if (!builder.BlobName.Equals(Name, StringComparison.InvariantCulture)) + { + throw Errors.SasNamesNotMatching( + nameof(builder.BlobName), + nameof(BlobSasBuilder), + nameof(Name)); + } + if (string.Compare(_snapshot, builder.Snapshot, StringComparison.InvariantCulture) != 0) + { + throw Errors.SasNamesNotMatching( + nameof(builder.Snapshot), + nameof(BlobSasBuilder)); + } + if (string.Compare(_blobVersionId, builder.BlobVersionId, StringComparison.InvariantCulture) != 0) + { + throw Errors.SasNamesNotMatching( + nameof(builder.BlobVersionId), + nameof(BlobSasBuilder)); + } + } } /// diff --git a/sdk/storage/Azure.Storage.Blobs/src/BlobClientOptions.cs b/sdk/storage/Azure.Storage.Blobs/src/BlobClientOptions.cs index b9167baec00dd..b16cefc83a535 100644 --- a/sdk/storage/Azure.Storage.Blobs/src/BlobClientOptions.cs +++ b/sdk/storage/Azure.Storage.Blobs/src/BlobClientOptions.cs @@ -151,7 +151,12 @@ public enum ServiceVersion /// /// The 2024-11-04 service version. /// - V2024_11_04 = 24 + V2024_11_04 = 24, + + /// + /// The 2025-01-05 service version. + /// + V2025_01_05 = 25 #pragma warning restore CA1707 // Identifiers should not contain underscores } diff --git a/sdk/storage/Azure.Storage.Blobs/src/BlobContainerClient.cs b/sdk/storage/Azure.Storage.Blobs/src/BlobContainerClient.cs index 97b6985c51918..8b641d58d2fee 100644 --- a/sdk/storage/Azure.Storage.Blobs/src/BlobContainerClient.cs +++ b/sdk/storage/Azure.Storage.Blobs/src/BlobContainerClient.cs @@ -3801,26 +3801,160 @@ public virtual Uri GenerateSasUri(BlobSasBuilder builder, out string stringToSig // Deep copy of builder so we don't modify the user's origial BlobSasBuilder. builder = BlobSasBuilder.DeepCopy(builder); - // Assign builder's ContainerName if it is null. - builder.BlobContainerName ??= Name; - - if (!builder.BlobContainerName.Equals(Name, StringComparison.InvariantCulture)) + SetBuilderAndValidate(builder); + BlobUriBuilder sasUri = new BlobUriBuilder(Uri, ClientConfiguration.TrimBlobNameSlashes) { - throw Errors.SasNamesNotMatching( - nameof(builder.BlobContainerName), - nameof(BlobSasBuilder), - nameof(Name)); - } - if (!string.IsNullOrEmpty(builder.BlobName)) + Sas = builder.ToSasQueryParameters(ClientConfiguration.SharedKeyCredential, out stringToSign) + }; + return sasUri.ToUri(); + } + #endregion + + #region GenerateUserDelegationSas + /// + /// The + /// returns a representing a Blob Container Service + /// Shared Access Signature (SAS) Uri based on the Client properties + /// and parameters passed. The SAS is signed by the user delegation key + /// that is passed in. + /// + /// For more information, see + /// + /// Creating an user delegation SAS. + /// + /// + /// Required. Specifies the list of permissions to be associated with the SAS. + /// See . + /// + /// + /// Required. Specifies the time at which the SAS becomes invalid. This field + /// must be omitted if it has been specified in an associated stored access policy. + /// + /// + /// Required. A returned from + /// . + /// + /// + /// A containing the SAS Uri. + /// + /// + /// A will be thrown if a failure occurs. + /// + [CallerShouldAudit("https://aka.ms/azsdk/callershouldaudit/storage-blobs")] + public virtual Uri GenerateUserDelegationSasUri(BlobContainerSasPermissions permissions, DateTimeOffset expiresOn, UserDelegationKey userDelegationKey) => + GenerateUserDelegationSasUri(permissions, expiresOn, userDelegationKey, out _); + + /// + /// The + /// returns a representing a Blob Container Service + /// Shared Access Signature (SAS) Uri based on the Client properties + /// and parameters passed. The SAS is signed by the user delegation key + /// that is passed in. + /// + /// For more information, see + /// + /// Creating an user delegation SAS. + /// + /// + /// Required. Specifies the list of permissions to be associated with the SAS. + /// See . + /// + /// + /// Required. Specifies the time at which the SAS becomes invalid. This field + /// must be omitted if it has been specified in an associated stored access policy. + /// + /// + /// Required. A returned from + /// . + /// + /// + /// For debugging purposes only. This string will be overwritten with the string to sign that was used to generate the SAS Uri. + /// + /// + /// A containing the SAS Uri. + /// + /// + /// A will be thrown if a failure occurs. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + [CallerShouldAudit("https://aka.ms/azsdk/callershouldaudit/storage-blobs")] + public virtual Uri GenerateUserDelegationSasUri(BlobContainerSasPermissions permissions, DateTimeOffset expiresOn, UserDelegationKey userDelegationKey, out string stringToSign) => + GenerateUserDelegationSasUri(new BlobSasBuilder(permissions, expiresOn) { BlobContainerName = Name }, userDelegationKey, out stringToSign); + + /// + /// The + /// returns a representing a Blob Container Service + /// Shared Access Signature (SAS) Uri based on the Client properties + /// and builder passed. The SAS is signed by the user delegation key + /// that is passed in. + /// + /// For more information, see + /// + /// Creating an user delegation SAS. + /// + /// + /// Required. Used to generate a Shared Access Signature (SAS). + /// + /// + /// Required. A returned from + /// . + /// + /// + /// A containing the SAS Uri. + /// + /// + /// A will be thrown if a failure occurs. + /// + [CallerShouldAudit("https://aka.ms/azsdk/callershouldaudit/storage-blobs")] + public virtual Uri GenerateUserDelegationSasUri(BlobSasBuilder builder, UserDelegationKey userDelegationKey) => + GenerateUserDelegationSasUri(builder, userDelegationKey, out _); + + /// + /// The + /// returns a representing a Blob Container Service + /// Shared Access Signature (SAS) Uri based on the Client properties + /// and builder passed. The SAS is signed by the user delegation key + /// that is passed in. + /// + /// For more information, see + /// + /// Creating an user delegation SAS. + /// + /// + /// Required. Used to generate a Shared Access Signature (SAS). + /// + /// + /// Required. A returned from + /// . + /// + /// + /// For debugging purposes only. This string will be overwritten with the string to sign that was used to generate the SAS Uri. + /// + /// + /// A containing the SAS Uri. + /// + /// + /// A will be thrown if a failure occurs. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + [CallerShouldAudit("https://aka.ms/azsdk/callershouldaudit/storage-blobs")] + public virtual Uri GenerateUserDelegationSasUri(BlobSasBuilder builder, UserDelegationKey userDelegationKey, out string stringToSign) + { + builder = builder ?? throw Errors.ArgumentNull(nameof(builder)); + userDelegationKey = userDelegationKey ?? throw Errors.ArgumentNull(nameof(userDelegationKey)); + + // Deep copy of builder so we don't modify the user's origial BlobSasBuilder. + builder = BlobSasBuilder.DeepCopy(builder); + + SetBuilderAndValidate(builder); + if (string.IsNullOrEmpty(AccountName)) { - throw Errors.SasBuilderEmptyParam( - nameof(builder), - nameof(builder.BlobName), - nameof(Constants.Blob.Container.Name)); + throw Errors.SasClientMissingData(nameof(AccountName)); } + BlobUriBuilder sasUri = new BlobUriBuilder(Uri, ClientConfiguration.TrimBlobNameSlashes) { - Sas = builder.ToSasQueryParameters(ClientConfiguration.SharedKeyCredential, out stringToSign) + Sas = builder.ToSasQueryParameters(userDelegationKey, AccountName, out stringToSign) }; return sasUri.ToUri(); } @@ -3860,6 +3994,28 @@ protected internal virtual BlobServiceClient GetParentBlobServiceClientCore() return _parentBlobServiceClient; } #endregion + + private void SetBuilderAndValidate(BlobSasBuilder builder) + { + // Assign builder's ContainerName if it is null. + builder.BlobContainerName ??= Name; + + // Validate that builder is properly set + if (!builder.BlobContainerName.Equals(Name, StringComparison.InvariantCulture)) + { + throw Errors.SasNamesNotMatching( + nameof(builder.BlobContainerName), + nameof(BlobSasBuilder), + nameof(Name)); + } + if (!string.IsNullOrEmpty(builder.BlobName)) + { + throw Errors.SasBuilderEmptyParam( + nameof(builder), + nameof(builder.BlobName), + nameof(Constants.Blob.Container.Name)); + } + } } namespace Specialized diff --git a/sdk/storage/Azure.Storage.Blobs/src/Generated/AppendBlobAppendBlockHeaders.cs b/sdk/storage/Azure.Storage.Blobs/src/Generated/AppendBlobAppendBlockHeaders.cs index 9303ec3a3d653..48139cc16a682 100644 --- a/sdk/storage/Azure.Storage.Blobs/src/Generated/AppendBlobAppendBlockHeaders.cs +++ b/sdk/storage/Azure.Storage.Blobs/src/Generated/AppendBlobAppendBlockHeaders.cs @@ -35,5 +35,7 @@ public AppendBlobAppendBlockHeaders(Response response) public string EncryptionKeySha256 => _response.Headers.TryGetValue("x-ms-encryption-key-sha256", out string value) ? value : null; /// Returns the name of the encryption scope used to encrypt the blob contents and application metadata. Note that the absence of this header implies use of the default account encryption scope. public string EncryptionScope => _response.Headers.TryGetValue("x-ms-encryption-scope", out string value) ? value : null; + /// Indicates the structured message body was accepted and mirrors back the message schema version and properties. + public string StructuredBodyType => _response.Headers.TryGetValue("x-ms-structured-body", out string value) ? value : null; } } diff --git a/sdk/storage/Azure.Storage.Blobs/src/Generated/AppendBlobRestClient.cs b/sdk/storage/Azure.Storage.Blobs/src/Generated/AppendBlobRestClient.cs index 88104aa95bb00..a3d0eca1ec405 100644 --- a/sdk/storage/Azure.Storage.Blobs/src/Generated/AppendBlobRestClient.cs +++ b/sdk/storage/Azure.Storage.Blobs/src/Generated/AppendBlobRestClient.cs @@ -29,7 +29,7 @@ internal partial class AppendBlobRestClient /// The handler for diagnostic messaging in the client. /// The HTTP pipeline for sending and receiving REST requests and responses. /// The URL of the service account, container, or blob that is the target of the desired operation. - /// Specifies the version of the operation to use for this request. The default value is "2024-08-04". + /// Specifies the version of the operation to use for this request. The default value is "2025-01-05". /// , , or is null. public AppendBlobRestClient(ClientDiagnostics clientDiagnostics, HttpPipeline pipeline, string url, string version) { @@ -219,7 +219,7 @@ public ResponseWithHeaders Create(long contentLength, i } } - internal HttpMessage CreateAppendBlockRequest(long contentLength, Stream body, int? timeout, byte[] transactionalContentMD5, byte[] transactionalContentCrc64, string leaseId, long? maxSize, long? appendPosition, string encryptionKey, string encryptionKeySha256, EncryptionAlgorithmTypeInternal? encryptionAlgorithm, string encryptionScope, DateTimeOffset? ifModifiedSince, DateTimeOffset? ifUnmodifiedSince, string ifMatch, string ifNoneMatch, string ifTags) + internal HttpMessage CreateAppendBlockRequest(long contentLength, Stream body, int? timeout, byte[] transactionalContentMD5, byte[] transactionalContentCrc64, string leaseId, long? maxSize, long? appendPosition, string encryptionKey, string encryptionKeySha256, EncryptionAlgorithmTypeInternal? encryptionAlgorithm, string encryptionScope, DateTimeOffset? ifModifiedSince, DateTimeOffset? ifUnmodifiedSince, string ifMatch, string ifNoneMatch, string ifTags, string structuredBodyType, long? structuredContentLength) { var message = _pipeline.CreateMessage(); var request = message.Request; @@ -285,6 +285,14 @@ internal HttpMessage CreateAppendBlockRequest(long contentLength, Stream body, i request.Headers.Add("x-ms-if-tags", ifTags); } request.Headers.Add("x-ms-version", _version); + if (structuredBodyType != null) + { + request.Headers.Add("x-ms-structured-body", structuredBodyType); + } + if (structuredContentLength != null) + { + request.Headers.Add("x-ms-structured-content-length", structuredContentLength.Value); + } request.Headers.Add("Accept", "application/xml"); request.Headers.Add("Content-Length", contentLength); if (transactionalContentMD5 != null) @@ -314,16 +322,18 @@ internal HttpMessage CreateAppendBlockRequest(long contentLength, Stream body, i /// Specify an ETag value to operate only on blobs with a matching value. /// Specify an ETag value to operate only on blobs without a matching value. /// Specify a SQL where clause on blob tags to operate only on blobs with a matching value. + /// Required if the request body is a structured message. Specifies the message schema version and properties. + /// Required if the request body is a structured message. Specifies the length of the blob/file content inside the message body. Will always be smaller than Content-Length. /// The cancellation token to use. /// is null. - public async Task> AppendBlockAsync(long contentLength, Stream body, int? timeout = null, byte[] transactionalContentMD5 = null, byte[] transactionalContentCrc64 = null, string leaseId = null, long? maxSize = null, long? appendPosition = null, string encryptionKey = null, string encryptionKeySha256 = null, EncryptionAlgorithmTypeInternal? encryptionAlgorithm = null, string encryptionScope = null, DateTimeOffset? ifModifiedSince = null, DateTimeOffset? ifUnmodifiedSince = null, string ifMatch = null, string ifNoneMatch = null, string ifTags = null, CancellationToken cancellationToken = default) + public async Task> AppendBlockAsync(long contentLength, Stream body, int? timeout = null, byte[] transactionalContentMD5 = null, byte[] transactionalContentCrc64 = null, string leaseId = null, long? maxSize = null, long? appendPosition = null, string encryptionKey = null, string encryptionKeySha256 = null, EncryptionAlgorithmTypeInternal? encryptionAlgorithm = null, string encryptionScope = null, DateTimeOffset? ifModifiedSince = null, DateTimeOffset? ifUnmodifiedSince = null, string ifMatch = null, string ifNoneMatch = null, string ifTags = null, string structuredBodyType = null, long? structuredContentLength = null, CancellationToken cancellationToken = default) { if (body == null) { throw new ArgumentNullException(nameof(body)); } - using var message = CreateAppendBlockRequest(contentLength, body, timeout, transactionalContentMD5, transactionalContentCrc64, leaseId, maxSize, appendPosition, encryptionKey, encryptionKeySha256, encryptionAlgorithm, encryptionScope, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, ifTags); + using var message = CreateAppendBlockRequest(contentLength, body, timeout, transactionalContentMD5, transactionalContentCrc64, leaseId, maxSize, appendPosition, encryptionKey, encryptionKeySha256, encryptionAlgorithm, encryptionScope, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, ifTags, structuredBodyType, structuredContentLength); await _pipeline.SendAsync(message, cancellationToken).ConfigureAwait(false); var headers = new AppendBlobAppendBlockHeaders(message.Response); switch (message.Response.Status) @@ -353,16 +363,18 @@ public async Task> AppendBlock /// Specify an ETag value to operate only on blobs with a matching value. /// Specify an ETag value to operate only on blobs without a matching value. /// Specify a SQL where clause on blob tags to operate only on blobs with a matching value. + /// Required if the request body is a structured message. Specifies the message schema version and properties. + /// Required if the request body is a structured message. Specifies the length of the blob/file content inside the message body. Will always be smaller than Content-Length. /// The cancellation token to use. /// is null. - public ResponseWithHeaders AppendBlock(long contentLength, Stream body, int? timeout = null, byte[] transactionalContentMD5 = null, byte[] transactionalContentCrc64 = null, string leaseId = null, long? maxSize = null, long? appendPosition = null, string encryptionKey = null, string encryptionKeySha256 = null, EncryptionAlgorithmTypeInternal? encryptionAlgorithm = null, string encryptionScope = null, DateTimeOffset? ifModifiedSince = null, DateTimeOffset? ifUnmodifiedSince = null, string ifMatch = null, string ifNoneMatch = null, string ifTags = null, CancellationToken cancellationToken = default) + public ResponseWithHeaders AppendBlock(long contentLength, Stream body, int? timeout = null, byte[] transactionalContentMD5 = null, byte[] transactionalContentCrc64 = null, string leaseId = null, long? maxSize = null, long? appendPosition = null, string encryptionKey = null, string encryptionKeySha256 = null, EncryptionAlgorithmTypeInternal? encryptionAlgorithm = null, string encryptionScope = null, DateTimeOffset? ifModifiedSince = null, DateTimeOffset? ifUnmodifiedSince = null, string ifMatch = null, string ifNoneMatch = null, string ifTags = null, string structuredBodyType = null, long? structuredContentLength = null, CancellationToken cancellationToken = default) { if (body == null) { throw new ArgumentNullException(nameof(body)); } - using var message = CreateAppendBlockRequest(contentLength, body, timeout, transactionalContentMD5, transactionalContentCrc64, leaseId, maxSize, appendPosition, encryptionKey, encryptionKeySha256, encryptionAlgorithm, encryptionScope, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, ifTags); + using var message = CreateAppendBlockRequest(contentLength, body, timeout, transactionalContentMD5, transactionalContentCrc64, leaseId, maxSize, appendPosition, encryptionKey, encryptionKeySha256, encryptionAlgorithm, encryptionScope, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, ifTags, structuredBodyType, structuredContentLength); _pipeline.Send(message, cancellationToken); var headers = new AppendBlobAppendBlockHeaders(message.Response); switch (message.Response.Status) diff --git a/sdk/storage/Azure.Storage.Blobs/src/Generated/BlobDownloadHeaders.cs b/sdk/storage/Azure.Storage.Blobs/src/Generated/BlobDownloadHeaders.cs index ad17079901a72..1897117cb01d8 100644 --- a/sdk/storage/Azure.Storage.Blobs/src/Generated/BlobDownloadHeaders.cs +++ b/sdk/storage/Azure.Storage.Blobs/src/Generated/BlobDownloadHeaders.cs @@ -96,6 +96,10 @@ public BlobDownloadHeaders(Response response) public BlobImmutabilityPolicyMode? ImmutabilityPolicyMode => _response.Headers.TryGetValue("x-ms-immutability-policy-mode", out string value) ? value.ToBlobImmutabilityPolicyMode() : null; /// Indicates if a legal hold is present on the blob. public bool? LegalHold => _response.Headers.TryGetValue("x-ms-legal-hold", out bool? value) ? value : null; + /// Indicates the response body contains a structured message and specifies the message schema version and properties. + public string StructuredBodyType => _response.Headers.TryGetValue("x-ms-structured-body", out string value) ? value : null; + /// The length of the blob/file content inside the message body when the response body is returned as a structured message. Will always be smaller than Content-Length. + public long? StructuredContentLength => _response.Headers.TryGetValue("x-ms-structured-content-length", out long? value) ? value : null; /// If the request is to read a specified range and the x-ms-range-get-content-crc64 is set to true, then the request returns a crc64 for the range, as long as the range size is less than or equal to 4 MB. If both x-ms-range-get-content-crc64 & x-ms-range-get-content-md5 is specified in the same request, it will fail with 400(Bad Request). public byte[] ContentCrc64 => _response.Headers.TryGetValue("x-ms-content-crc64", out byte[] value) ? value : null; public string ErrorCode => _response.Headers.TryGetValue("x-ms-error-code", out string value) ? value : null; diff --git a/sdk/storage/Azure.Storage.Blobs/src/Generated/BlobRestClient.cs b/sdk/storage/Azure.Storage.Blobs/src/Generated/BlobRestClient.cs index 615257741b781..2702d622e4bd8 100644 --- a/sdk/storage/Azure.Storage.Blobs/src/Generated/BlobRestClient.cs +++ b/sdk/storage/Azure.Storage.Blobs/src/Generated/BlobRestClient.cs @@ -30,7 +30,7 @@ internal partial class BlobRestClient /// The handler for diagnostic messaging in the client. /// The HTTP pipeline for sending and receiving REST requests and responses. /// The URL of the service account, container, or blob that is the target of the desired operation. - /// Specifies the version of the operation to use for this request. The default value is "2024-08-04". + /// Specifies the version of the operation to use for this request. The default value is "2025-01-05". /// , , or is null. public BlobRestClient(ClientDiagnostics clientDiagnostics, HttpPipeline pipeline, string url, string version) { @@ -40,7 +40,7 @@ public BlobRestClient(ClientDiagnostics clientDiagnostics, HttpPipeline pipeline _version = version ?? throw new ArgumentNullException(nameof(version)); } - internal HttpMessage CreateDownloadRequest(string snapshot, string versionId, int? timeout, string range, string leaseId, bool? rangeGetContentMD5, bool? rangeGetContentCRC64, string encryptionKey, string encryptionKeySha256, EncryptionAlgorithmTypeInternal? encryptionAlgorithm, DateTimeOffset? ifModifiedSince, DateTimeOffset? ifUnmodifiedSince, string ifMatch, string ifNoneMatch, string ifTags) + internal HttpMessage CreateDownloadRequest(string snapshot, string versionId, int? timeout, string range, string leaseId, bool? rangeGetContentMD5, bool? rangeGetContentCRC64, string structuredBodyType, string encryptionKey, string encryptionKeySha256, EncryptionAlgorithmTypeInternal? encryptionAlgorithm, DateTimeOffset? ifModifiedSince, DateTimeOffset? ifUnmodifiedSince, string ifMatch, string ifNoneMatch, string ifTags) { var message = _pipeline.CreateMessage(); var request = message.Request; @@ -77,6 +77,10 @@ internal HttpMessage CreateDownloadRequest(string snapshot, string versionId, in { request.Headers.Add("x-ms-range-get-content-crc64", rangeGetContentCRC64.Value); } + if (structuredBodyType != null) + { + request.Headers.Add("x-ms-structured-body", structuredBodyType); + } if (encryptionKey != null) { request.Headers.Add("x-ms-encryption-key", encryptionKey); @@ -122,6 +126,7 @@ internal HttpMessage CreateDownloadRequest(string snapshot, string versionId, in /// If specified, the operation only succeeds if the resource's lease is active and matches this ID. /// When set to true and specified together with the Range, the service returns the MD5 hash for the range, as long as the range is less than or equal to 4 MB in size. /// When set to true and specified together with the Range, the service returns the CRC64 hash for the range, as long as the range is less than or equal to 4 MB in size. + /// Specifies the response content should be returned as a structured message and specifies the message schema version and properties. /// Optional. Specifies the encryption key to use to encrypt the data provided in the request. If not specified, encryption is performed with the root account encryption key. For more information, see Encryption at Rest for Azure Storage Services. /// The SHA-256 hash of the provided encryption key. Must be provided if the x-ms-encryption-key header is provided. /// The algorithm used to produce the encryption key hash. Currently, the only accepted value is "AES256". Must be provided if the x-ms-encryption-key header is provided. @@ -131,9 +136,9 @@ internal HttpMessage CreateDownloadRequest(string snapshot, string versionId, in /// Specify an ETag value to operate only on blobs without a matching value. /// Specify a SQL where clause on blob tags to operate only on blobs with a matching value. /// The cancellation token to use. - public async Task> DownloadAsync(string snapshot = null, string versionId = null, int? timeout = null, string range = null, string leaseId = null, bool? rangeGetContentMD5 = null, bool? rangeGetContentCRC64 = null, string encryptionKey = null, string encryptionKeySha256 = null, EncryptionAlgorithmTypeInternal? encryptionAlgorithm = null, DateTimeOffset? ifModifiedSince = null, DateTimeOffset? ifUnmodifiedSince = null, string ifMatch = null, string ifNoneMatch = null, string ifTags = null, CancellationToken cancellationToken = default) + public async Task> DownloadAsync(string snapshot = null, string versionId = null, int? timeout = null, string range = null, string leaseId = null, bool? rangeGetContentMD5 = null, bool? rangeGetContentCRC64 = null, string structuredBodyType = null, string encryptionKey = null, string encryptionKeySha256 = null, EncryptionAlgorithmTypeInternal? encryptionAlgorithm = null, DateTimeOffset? ifModifiedSince = null, DateTimeOffset? ifUnmodifiedSince = null, string ifMatch = null, string ifNoneMatch = null, string ifTags = null, CancellationToken cancellationToken = default) { - using var message = CreateDownloadRequest(snapshot, versionId, timeout, range, leaseId, rangeGetContentMD5, rangeGetContentCRC64, encryptionKey, encryptionKeySha256, encryptionAlgorithm, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, ifTags); + using var message = CreateDownloadRequest(snapshot, versionId, timeout, range, leaseId, rangeGetContentMD5, rangeGetContentCRC64, structuredBodyType, encryptionKey, encryptionKeySha256, encryptionAlgorithm, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, ifTags); await _pipeline.SendAsync(message, cancellationToken).ConfigureAwait(false); var headers = new BlobDownloadHeaders(message.Response); switch (message.Response.Status) @@ -159,6 +164,7 @@ public async Task> DownloadAsyn /// If specified, the operation only succeeds if the resource's lease is active and matches this ID. /// When set to true and specified together with the Range, the service returns the MD5 hash for the range, as long as the range is less than or equal to 4 MB in size. /// When set to true and specified together with the Range, the service returns the CRC64 hash for the range, as long as the range is less than or equal to 4 MB in size. + /// Specifies the response content should be returned as a structured message and specifies the message schema version and properties. /// Optional. Specifies the encryption key to use to encrypt the data provided in the request. If not specified, encryption is performed with the root account encryption key. For more information, see Encryption at Rest for Azure Storage Services. /// The SHA-256 hash of the provided encryption key. Must be provided if the x-ms-encryption-key header is provided. /// The algorithm used to produce the encryption key hash. Currently, the only accepted value is "AES256". Must be provided if the x-ms-encryption-key header is provided. @@ -168,9 +174,9 @@ public async Task> DownloadAsyn /// Specify an ETag value to operate only on blobs without a matching value. /// Specify a SQL where clause on blob tags to operate only on blobs with a matching value. /// The cancellation token to use. - public ResponseWithHeaders Download(string snapshot = null, string versionId = null, int? timeout = null, string range = null, string leaseId = null, bool? rangeGetContentMD5 = null, bool? rangeGetContentCRC64 = null, string encryptionKey = null, string encryptionKeySha256 = null, EncryptionAlgorithmTypeInternal? encryptionAlgorithm = null, DateTimeOffset? ifModifiedSince = null, DateTimeOffset? ifUnmodifiedSince = null, string ifMatch = null, string ifNoneMatch = null, string ifTags = null, CancellationToken cancellationToken = default) + public ResponseWithHeaders Download(string snapshot = null, string versionId = null, int? timeout = null, string range = null, string leaseId = null, bool? rangeGetContentMD5 = null, bool? rangeGetContentCRC64 = null, string structuredBodyType = null, string encryptionKey = null, string encryptionKeySha256 = null, EncryptionAlgorithmTypeInternal? encryptionAlgorithm = null, DateTimeOffset? ifModifiedSince = null, DateTimeOffset? ifUnmodifiedSince = null, string ifMatch = null, string ifNoneMatch = null, string ifTags = null, CancellationToken cancellationToken = default) { - using var message = CreateDownloadRequest(snapshot, versionId, timeout, range, leaseId, rangeGetContentMD5, rangeGetContentCRC64, encryptionKey, encryptionKeySha256, encryptionAlgorithm, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, ifTags); + using var message = CreateDownloadRequest(snapshot, versionId, timeout, range, leaseId, rangeGetContentMD5, rangeGetContentCRC64, structuredBodyType, encryptionKey, encryptionKeySha256, encryptionAlgorithm, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, ifTags); _pipeline.Send(message, cancellationToken); var headers = new BlobDownloadHeaders(message.Response); switch (message.Response.Status) @@ -778,7 +784,7 @@ public ResponseWithHeaders SetHttpHeaders(int? timeou } } - internal HttpMessage CreateSetImmutabilityPolicyRequest(int? timeout, DateTimeOffset? ifUnmodifiedSince, DateTimeOffset? immutabilityPolicyExpiry, BlobImmutabilityPolicyMode? immutabilityPolicyMode) + internal HttpMessage CreateSetImmutabilityPolicyRequest(int? timeout, DateTimeOffset? ifUnmodifiedSince, DateTimeOffset? immutabilityPolicyExpiry, BlobImmutabilityPolicyMode? immutabilityPolicyMode, string snapshot, string versionId) { var message = _pipeline.CreateMessage(); var request = message.Request; @@ -790,6 +796,14 @@ internal HttpMessage CreateSetImmutabilityPolicyRequest(int? timeout, DateTimeOf { uri.AppendQuery("timeout", timeout.Value, true); } + if (snapshot != null) + { + uri.AppendQuery("snapshot", snapshot, true); + } + if (versionId != null) + { + uri.AppendQuery("versionid", versionId, true); + } request.Uri = uri; request.Headers.Add("x-ms-version", _version); if (ifUnmodifiedSince != null) @@ -813,10 +827,12 @@ internal HttpMessage CreateSetImmutabilityPolicyRequest(int? timeout, DateTimeOf /// Specify this header value to operate only on a blob if it has not been modified since the specified date/time. /// Specifies the date time when the blobs immutability policy is set to expire. /// Specifies the immutability policy mode to set on the blob. + /// The snapshot parameter is an opaque DateTime value that, when present, specifies the blob snapshot to retrieve. For more information on working with blob snapshots, see <a href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/creating-a-snapshot-of-a-blob">Creating a Snapshot of a Blob.</a>. + /// The version id parameter is an opaque DateTime value that, when present, specifies the version of the blob to operate on. It's for service version 2019-10-10 and newer. /// The cancellation token to use. - public async Task> SetImmutabilityPolicyAsync(int? timeout = null, DateTimeOffset? ifUnmodifiedSince = null, DateTimeOffset? immutabilityPolicyExpiry = null, BlobImmutabilityPolicyMode? immutabilityPolicyMode = null, CancellationToken cancellationToken = default) + public async Task> SetImmutabilityPolicyAsync(int? timeout = null, DateTimeOffset? ifUnmodifiedSince = null, DateTimeOffset? immutabilityPolicyExpiry = null, BlobImmutabilityPolicyMode? immutabilityPolicyMode = null, string snapshot = null, string versionId = null, CancellationToken cancellationToken = default) { - using var message = CreateSetImmutabilityPolicyRequest(timeout, ifUnmodifiedSince, immutabilityPolicyExpiry, immutabilityPolicyMode); + using var message = CreateSetImmutabilityPolicyRequest(timeout, ifUnmodifiedSince, immutabilityPolicyExpiry, immutabilityPolicyMode, snapshot, versionId); await _pipeline.SendAsync(message, cancellationToken).ConfigureAwait(false); var headers = new BlobSetImmutabilityPolicyHeaders(message.Response); switch (message.Response.Status) @@ -833,10 +849,12 @@ public async Task> SetImmu /// Specify this header value to operate only on a blob if it has not been modified since the specified date/time. /// Specifies the date time when the blobs immutability policy is set to expire. /// Specifies the immutability policy mode to set on the blob. + /// The snapshot parameter is an opaque DateTime value that, when present, specifies the blob snapshot to retrieve. For more information on working with blob snapshots, see <a href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/creating-a-snapshot-of-a-blob">Creating a Snapshot of a Blob.</a>. + /// The version id parameter is an opaque DateTime value that, when present, specifies the version of the blob to operate on. It's for service version 2019-10-10 and newer. /// The cancellation token to use. - public ResponseWithHeaders SetImmutabilityPolicy(int? timeout = null, DateTimeOffset? ifUnmodifiedSince = null, DateTimeOffset? immutabilityPolicyExpiry = null, BlobImmutabilityPolicyMode? immutabilityPolicyMode = null, CancellationToken cancellationToken = default) + public ResponseWithHeaders SetImmutabilityPolicy(int? timeout = null, DateTimeOffset? ifUnmodifiedSince = null, DateTimeOffset? immutabilityPolicyExpiry = null, BlobImmutabilityPolicyMode? immutabilityPolicyMode = null, string snapshot = null, string versionId = null, CancellationToken cancellationToken = default) { - using var message = CreateSetImmutabilityPolicyRequest(timeout, ifUnmodifiedSince, immutabilityPolicyExpiry, immutabilityPolicyMode); + using var message = CreateSetImmutabilityPolicyRequest(timeout, ifUnmodifiedSince, immutabilityPolicyExpiry, immutabilityPolicyMode, snapshot, versionId); _pipeline.Send(message, cancellationToken); var headers = new BlobSetImmutabilityPolicyHeaders(message.Response); switch (message.Response.Status) @@ -848,7 +866,7 @@ public ResponseWithHeaders SetImmutabilityPoli } } - internal HttpMessage CreateDeleteImmutabilityPolicyRequest(int? timeout) + internal HttpMessage CreateDeleteImmutabilityPolicyRequest(int? timeout, string snapshot, string versionId) { var message = _pipeline.CreateMessage(); var request = message.Request; @@ -860,6 +878,14 @@ internal HttpMessage CreateDeleteImmutabilityPolicyRequest(int? timeout) { uri.AppendQuery("timeout", timeout.Value, true); } + if (snapshot != null) + { + uri.AppendQuery("snapshot", snapshot, true); + } + if (versionId != null) + { + uri.AppendQuery("versionid", versionId, true); + } request.Uri = uri; request.Headers.Add("x-ms-version", _version); request.Headers.Add("Accept", "application/xml"); @@ -868,10 +894,12 @@ internal HttpMessage CreateDeleteImmutabilityPolicyRequest(int? timeout) /// The Delete Immutability Policy operation deletes the immutability policy on the blob. /// The timeout parameter is expressed in seconds. For more information, see <a href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations">Setting Timeouts for Blob Service Operations.</a>. + /// The snapshot parameter is an opaque DateTime value that, when present, specifies the blob snapshot to retrieve. For more information on working with blob snapshots, see <a href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/creating-a-snapshot-of-a-blob">Creating a Snapshot of a Blob.</a>. + /// The version id parameter is an opaque DateTime value that, when present, specifies the version of the blob to operate on. It's for service version 2019-10-10 and newer. /// The cancellation token to use. - public async Task> DeleteImmutabilityPolicyAsync(int? timeout = null, CancellationToken cancellationToken = default) + public async Task> DeleteImmutabilityPolicyAsync(int? timeout = null, string snapshot = null, string versionId = null, CancellationToken cancellationToken = default) { - using var message = CreateDeleteImmutabilityPolicyRequest(timeout); + using var message = CreateDeleteImmutabilityPolicyRequest(timeout, snapshot, versionId); await _pipeline.SendAsync(message, cancellationToken).ConfigureAwait(false); var headers = new BlobDeleteImmutabilityPolicyHeaders(message.Response); switch (message.Response.Status) @@ -885,10 +913,12 @@ public async Task> Dele /// The Delete Immutability Policy operation deletes the immutability policy on the blob. /// The timeout parameter is expressed in seconds. For more information, see <a href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations">Setting Timeouts for Blob Service Operations.</a>. + /// The snapshot parameter is an opaque DateTime value that, when present, specifies the blob snapshot to retrieve. For more information on working with blob snapshots, see <a href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/creating-a-snapshot-of-a-blob">Creating a Snapshot of a Blob.</a>. + /// The version id parameter is an opaque DateTime value that, when present, specifies the version of the blob to operate on. It's for service version 2019-10-10 and newer. /// The cancellation token to use. - public ResponseWithHeaders DeleteImmutabilityPolicy(int? timeout = null, CancellationToken cancellationToken = default) + public ResponseWithHeaders DeleteImmutabilityPolicy(int? timeout = null, string snapshot = null, string versionId = null, CancellationToken cancellationToken = default) { - using var message = CreateDeleteImmutabilityPolicyRequest(timeout); + using var message = CreateDeleteImmutabilityPolicyRequest(timeout, snapshot, versionId); _pipeline.Send(message, cancellationToken); var headers = new BlobDeleteImmutabilityPolicyHeaders(message.Response); switch (message.Response.Status) @@ -900,7 +930,7 @@ public ResponseWithHeaders DeleteImmutabili } } - internal HttpMessage CreateSetLegalHoldRequest(bool legalHold, int? timeout) + internal HttpMessage CreateSetLegalHoldRequest(bool legalHold, int? timeout, string snapshot, string versionId) { var message = _pipeline.CreateMessage(); var request = message.Request; @@ -912,6 +942,14 @@ internal HttpMessage CreateSetLegalHoldRequest(bool legalHold, int? timeout) { uri.AppendQuery("timeout", timeout.Value, true); } + if (snapshot != null) + { + uri.AppendQuery("snapshot", snapshot, true); + } + if (versionId != null) + { + uri.AppendQuery("versionid", versionId, true); + } request.Uri = uri; request.Headers.Add("x-ms-version", _version); request.Headers.Add("x-ms-legal-hold", legalHold); @@ -922,10 +960,12 @@ internal HttpMessage CreateSetLegalHoldRequest(bool legalHold, int? timeout) /// The Set Legal Hold operation sets a legal hold on the blob. /// Specified if a legal hold should be set on the blob. /// The timeout parameter is expressed in seconds. For more information, see <a href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations">Setting Timeouts for Blob Service Operations.</a>. + /// The snapshot parameter is an opaque DateTime value that, when present, specifies the blob snapshot to retrieve. For more information on working with blob snapshots, see <a href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/creating-a-snapshot-of-a-blob">Creating a Snapshot of a Blob.</a>. + /// The version id parameter is an opaque DateTime value that, when present, specifies the version of the blob to operate on. It's for service version 2019-10-10 and newer. /// The cancellation token to use. - public async Task> SetLegalHoldAsync(bool legalHold, int? timeout = null, CancellationToken cancellationToken = default) + public async Task> SetLegalHoldAsync(bool legalHold, int? timeout = null, string snapshot = null, string versionId = null, CancellationToken cancellationToken = default) { - using var message = CreateSetLegalHoldRequest(legalHold, timeout); + using var message = CreateSetLegalHoldRequest(legalHold, timeout, snapshot, versionId); await _pipeline.SendAsync(message, cancellationToken).ConfigureAwait(false); var headers = new BlobSetLegalHoldHeaders(message.Response); switch (message.Response.Status) @@ -940,10 +980,12 @@ public async Task> SetLegalHoldAsyn /// The Set Legal Hold operation sets a legal hold on the blob. /// Specified if a legal hold should be set on the blob. /// The timeout parameter is expressed in seconds. For more information, see <a href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations">Setting Timeouts for Blob Service Operations.</a>. + /// The snapshot parameter is an opaque DateTime value that, when present, specifies the blob snapshot to retrieve. For more information on working with blob snapshots, see <a href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/creating-a-snapshot-of-a-blob">Creating a Snapshot of a Blob.</a>. + /// The version id parameter is an opaque DateTime value that, when present, specifies the version of the blob to operate on. It's for service version 2019-10-10 and newer. /// The cancellation token to use. - public ResponseWithHeaders SetLegalHold(bool legalHold, int? timeout = null, CancellationToken cancellationToken = default) + public ResponseWithHeaders SetLegalHold(bool legalHold, int? timeout = null, string snapshot = null, string versionId = null, CancellationToken cancellationToken = default) { - using var message = CreateSetLegalHoldRequest(legalHold, timeout); + using var message = CreateSetLegalHoldRequest(legalHold, timeout, snapshot, versionId); _pipeline.Send(message, cancellationToken); var headers = new BlobSetLegalHoldHeaders(message.Response); switch (message.Response.Status) diff --git a/sdk/storage/Azure.Storage.Blobs/src/Generated/BlockBlobRestClient.cs b/sdk/storage/Azure.Storage.Blobs/src/Generated/BlockBlobRestClient.cs index 0723c07204ac2..78ef424f66b13 100644 --- a/sdk/storage/Azure.Storage.Blobs/src/Generated/BlockBlobRestClient.cs +++ b/sdk/storage/Azure.Storage.Blobs/src/Generated/BlockBlobRestClient.cs @@ -30,7 +30,7 @@ internal partial class BlockBlobRestClient /// The handler for diagnostic messaging in the client. /// The HTTP pipeline for sending and receiving REST requests and responses. /// The URL of the service account, container, or blob that is the target of the desired operation. - /// Specifies the version of the operation to use for this request. The default value is "2024-08-04". + /// Specifies the version of the operation to use for this request. The default value is "2025-01-05". /// , , or is null. public BlockBlobRestClient(ClientDiagnostics clientDiagnostics, HttpPipeline pipeline, string url, string version) { @@ -40,7 +40,7 @@ public BlockBlobRestClient(ClientDiagnostics clientDiagnostics, HttpPipeline pip _version = version ?? throw new ArgumentNullException(nameof(version)); } - internal HttpMessage CreateUploadRequest(long contentLength, Stream body, int? timeout, byte[] transactionalContentMD5, string blobContentType, string blobContentEncoding, string blobContentLanguage, byte[] blobContentMD5, string blobCacheControl, IDictionary metadata, string leaseId, string blobContentDisposition, string encryptionKey, string encryptionKeySha256, EncryptionAlgorithmTypeInternal? encryptionAlgorithm, string encryptionScope, AccessTier? tier, DateTimeOffset? ifModifiedSince, DateTimeOffset? ifUnmodifiedSince, string ifMatch, string ifNoneMatch, string ifTags, string blobTagsString, DateTimeOffset? immutabilityPolicyExpiry, BlobImmutabilityPolicyMode? immutabilityPolicyMode, bool? legalHold, byte[] transactionalContentCrc64) + internal HttpMessage CreateUploadRequest(long contentLength, Stream body, int? timeout, byte[] transactionalContentMD5, string blobContentType, string blobContentEncoding, string blobContentLanguage, byte[] blobContentMD5, string blobCacheControl, IDictionary metadata, string leaseId, string blobContentDisposition, string encryptionKey, string encryptionKeySha256, EncryptionAlgorithmTypeInternal? encryptionAlgorithm, string encryptionScope, AccessTier? tier, DateTimeOffset? ifModifiedSince, DateTimeOffset? ifUnmodifiedSince, string ifMatch, string ifNoneMatch, string ifTags, string blobTagsString, DateTimeOffset? immutabilityPolicyExpiry, BlobImmutabilityPolicyMode? immutabilityPolicyMode, bool? legalHold, byte[] transactionalContentCrc64, string structuredBodyType, long? structuredContentLength) { var message = _pipeline.CreateMessage(); var request = message.Request; @@ -146,6 +146,14 @@ internal HttpMessage CreateUploadRequest(long contentLength, Stream body, int? t { request.Headers.Add("x-ms-content-crc64", transactionalContentCrc64, "D"); } + if (structuredBodyType != null) + { + request.Headers.Add("x-ms-structured-body", structuredBodyType); + } + if (structuredContentLength != null) + { + request.Headers.Add("x-ms-structured-content-length", structuredContentLength.Value); + } request.Headers.Add("Accept", "application/xml"); if (transactionalContentMD5 != null) { @@ -185,16 +193,18 @@ internal HttpMessage CreateUploadRequest(long contentLength, Stream body, int? t /// Specifies the immutability policy mode to set on the blob. /// Specified if a legal hold should be set on the blob. /// Specify the transactional crc64 for the body, to be validated by the service. + /// Required if the request body is a structured message. Specifies the message schema version and properties. + /// Required if the request body is a structured message. Specifies the length of the blob/file content inside the message body. Will always be smaller than Content-Length. /// The cancellation token to use. /// is null. - public async Task> UploadAsync(long contentLength, Stream body, int? timeout = null, byte[] transactionalContentMD5 = null, string blobContentType = null, string blobContentEncoding = null, string blobContentLanguage = null, byte[] blobContentMD5 = null, string blobCacheControl = null, IDictionary metadata = null, string leaseId = null, string blobContentDisposition = null, string encryptionKey = null, string encryptionKeySha256 = null, EncryptionAlgorithmTypeInternal? encryptionAlgorithm = null, string encryptionScope = null, AccessTier? tier = null, DateTimeOffset? ifModifiedSince = null, DateTimeOffset? ifUnmodifiedSince = null, string ifMatch = null, string ifNoneMatch = null, string ifTags = null, string blobTagsString = null, DateTimeOffset? immutabilityPolicyExpiry = null, BlobImmutabilityPolicyMode? immutabilityPolicyMode = null, bool? legalHold = null, byte[] transactionalContentCrc64 = null, CancellationToken cancellationToken = default) + public async Task> UploadAsync(long contentLength, Stream body, int? timeout = null, byte[] transactionalContentMD5 = null, string blobContentType = null, string blobContentEncoding = null, string blobContentLanguage = null, byte[] blobContentMD5 = null, string blobCacheControl = null, IDictionary metadata = null, string leaseId = null, string blobContentDisposition = null, string encryptionKey = null, string encryptionKeySha256 = null, EncryptionAlgorithmTypeInternal? encryptionAlgorithm = null, string encryptionScope = null, AccessTier? tier = null, DateTimeOffset? ifModifiedSince = null, DateTimeOffset? ifUnmodifiedSince = null, string ifMatch = null, string ifNoneMatch = null, string ifTags = null, string blobTagsString = null, DateTimeOffset? immutabilityPolicyExpiry = null, BlobImmutabilityPolicyMode? immutabilityPolicyMode = null, bool? legalHold = null, byte[] transactionalContentCrc64 = null, string structuredBodyType = null, long? structuredContentLength = null, CancellationToken cancellationToken = default) { if (body == null) { throw new ArgumentNullException(nameof(body)); } - using var message = CreateUploadRequest(contentLength, body, timeout, transactionalContentMD5, blobContentType, blobContentEncoding, blobContentLanguage, blobContentMD5, blobCacheControl, metadata, leaseId, blobContentDisposition, encryptionKey, encryptionKeySha256, encryptionAlgorithm, encryptionScope, tier, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, ifTags, blobTagsString, immutabilityPolicyExpiry, immutabilityPolicyMode, legalHold, transactionalContentCrc64); + using var message = CreateUploadRequest(contentLength, body, timeout, transactionalContentMD5, blobContentType, blobContentEncoding, blobContentLanguage, blobContentMD5, blobCacheControl, metadata, leaseId, blobContentDisposition, encryptionKey, encryptionKeySha256, encryptionAlgorithm, encryptionScope, tier, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, ifTags, blobTagsString, immutabilityPolicyExpiry, immutabilityPolicyMode, legalHold, transactionalContentCrc64, structuredBodyType, structuredContentLength); await _pipeline.SendAsync(message, cancellationToken).ConfigureAwait(false); var headers = new BlockBlobUploadHeaders(message.Response); switch (message.Response.Status) @@ -234,16 +244,18 @@ public async Task> UploadAsync(long /// Specifies the immutability policy mode to set on the blob. /// Specified if a legal hold should be set on the blob. /// Specify the transactional crc64 for the body, to be validated by the service. + /// Required if the request body is a structured message. Specifies the message schema version and properties. + /// Required if the request body is a structured message. Specifies the length of the blob/file content inside the message body. Will always be smaller than Content-Length. /// The cancellation token to use. /// is null. - public ResponseWithHeaders Upload(long contentLength, Stream body, int? timeout = null, byte[] transactionalContentMD5 = null, string blobContentType = null, string blobContentEncoding = null, string blobContentLanguage = null, byte[] blobContentMD5 = null, string blobCacheControl = null, IDictionary metadata = null, string leaseId = null, string blobContentDisposition = null, string encryptionKey = null, string encryptionKeySha256 = null, EncryptionAlgorithmTypeInternal? encryptionAlgorithm = null, string encryptionScope = null, AccessTier? tier = null, DateTimeOffset? ifModifiedSince = null, DateTimeOffset? ifUnmodifiedSince = null, string ifMatch = null, string ifNoneMatch = null, string ifTags = null, string blobTagsString = null, DateTimeOffset? immutabilityPolicyExpiry = null, BlobImmutabilityPolicyMode? immutabilityPolicyMode = null, bool? legalHold = null, byte[] transactionalContentCrc64 = null, CancellationToken cancellationToken = default) + public ResponseWithHeaders Upload(long contentLength, Stream body, int? timeout = null, byte[] transactionalContentMD5 = null, string blobContentType = null, string blobContentEncoding = null, string blobContentLanguage = null, byte[] blobContentMD5 = null, string blobCacheControl = null, IDictionary metadata = null, string leaseId = null, string blobContentDisposition = null, string encryptionKey = null, string encryptionKeySha256 = null, EncryptionAlgorithmTypeInternal? encryptionAlgorithm = null, string encryptionScope = null, AccessTier? tier = null, DateTimeOffset? ifModifiedSince = null, DateTimeOffset? ifUnmodifiedSince = null, string ifMatch = null, string ifNoneMatch = null, string ifTags = null, string blobTagsString = null, DateTimeOffset? immutabilityPolicyExpiry = null, BlobImmutabilityPolicyMode? immutabilityPolicyMode = null, bool? legalHold = null, byte[] transactionalContentCrc64 = null, string structuredBodyType = null, long? structuredContentLength = null, CancellationToken cancellationToken = default) { if (body == null) { throw new ArgumentNullException(nameof(body)); } - using var message = CreateUploadRequest(contentLength, body, timeout, transactionalContentMD5, blobContentType, blobContentEncoding, blobContentLanguage, blobContentMD5, blobCacheControl, metadata, leaseId, blobContentDisposition, encryptionKey, encryptionKeySha256, encryptionAlgorithm, encryptionScope, tier, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, ifTags, blobTagsString, immutabilityPolicyExpiry, immutabilityPolicyMode, legalHold, transactionalContentCrc64); + using var message = CreateUploadRequest(contentLength, body, timeout, transactionalContentMD5, blobContentType, blobContentEncoding, blobContentLanguage, blobContentMD5, blobCacheControl, metadata, leaseId, blobContentDisposition, encryptionKey, encryptionKeySha256, encryptionAlgorithm, encryptionScope, tier, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, ifTags, blobTagsString, immutabilityPolicyExpiry, immutabilityPolicyMode, legalHold, transactionalContentCrc64, structuredBodyType, structuredContentLength); _pipeline.Send(message, cancellationToken); var headers = new BlockBlobUploadHeaders(message.Response); switch (message.Response.Status) @@ -494,7 +506,7 @@ public ResponseWithHeaders PutBlobFromUrl(long c } } - internal HttpMessage CreateStageBlockRequest(string blockId, long contentLength, Stream body, byte[] transactionalContentMD5, byte[] transactionalContentCrc64, int? timeout, string leaseId, string encryptionKey, string encryptionKeySha256, EncryptionAlgorithmTypeInternal? encryptionAlgorithm, string encryptionScope) + internal HttpMessage CreateStageBlockRequest(string blockId, long contentLength, Stream body, byte[] transactionalContentMD5, byte[] transactionalContentCrc64, int? timeout, string leaseId, string encryptionKey, string encryptionKeySha256, EncryptionAlgorithmTypeInternal? encryptionAlgorithm, string encryptionScope, string structuredBodyType, long? structuredContentLength) { var message = _pipeline.CreateMessage(); var request = message.Request; @@ -533,6 +545,14 @@ internal HttpMessage CreateStageBlockRequest(string blockId, long contentLength, request.Headers.Add("x-ms-encryption-scope", encryptionScope); } request.Headers.Add("x-ms-version", _version); + if (structuredBodyType != null) + { + request.Headers.Add("x-ms-structured-body", structuredBodyType); + } + if (structuredContentLength != null) + { + request.Headers.Add("x-ms-structured-content-length", structuredContentLength.Value); + } request.Headers.Add("Accept", "application/xml"); request.Headers.Add("Content-Length", contentLength); if (transactionalContentMD5 != null) @@ -556,9 +576,11 @@ internal HttpMessage CreateStageBlockRequest(string blockId, long contentLength, /// The SHA-256 hash of the provided encryption key. Must be provided if the x-ms-encryption-key header is provided. /// The algorithm used to produce the encryption key hash. Currently, the only accepted value is "AES256". Must be provided if the x-ms-encryption-key header is provided. /// Optional. Version 2019-07-07 and later. Specifies the name of the encryption scope to use to encrypt the data provided in the request. If not specified, encryption is performed with the default account encryption scope. For more information, see Encryption at Rest for Azure Storage Services. + /// Required if the request body is a structured message. Specifies the message schema version and properties. + /// Required if the request body is a structured message. Specifies the length of the blob/file content inside the message body. Will always be smaller than Content-Length. /// The cancellation token to use. /// or is null. - public async Task> StageBlockAsync(string blockId, long contentLength, Stream body, byte[] transactionalContentMD5 = null, byte[] transactionalContentCrc64 = null, int? timeout = null, string leaseId = null, string encryptionKey = null, string encryptionKeySha256 = null, EncryptionAlgorithmTypeInternal? encryptionAlgorithm = null, string encryptionScope = null, CancellationToken cancellationToken = default) + public async Task> StageBlockAsync(string blockId, long contentLength, Stream body, byte[] transactionalContentMD5 = null, byte[] transactionalContentCrc64 = null, int? timeout = null, string leaseId = null, string encryptionKey = null, string encryptionKeySha256 = null, EncryptionAlgorithmTypeInternal? encryptionAlgorithm = null, string encryptionScope = null, string structuredBodyType = null, long? structuredContentLength = null, CancellationToken cancellationToken = default) { if (blockId == null) { @@ -569,7 +591,7 @@ public async Task> StageBlockAsy throw new ArgumentNullException(nameof(body)); } - using var message = CreateStageBlockRequest(blockId, contentLength, body, transactionalContentMD5, transactionalContentCrc64, timeout, leaseId, encryptionKey, encryptionKeySha256, encryptionAlgorithm, encryptionScope); + using var message = CreateStageBlockRequest(blockId, contentLength, body, transactionalContentMD5, transactionalContentCrc64, timeout, leaseId, encryptionKey, encryptionKeySha256, encryptionAlgorithm, encryptionScope, structuredBodyType, structuredContentLength); await _pipeline.SendAsync(message, cancellationToken).ConfigureAwait(false); var headers = new BlockBlobStageBlockHeaders(message.Response); switch (message.Response.Status) @@ -593,9 +615,11 @@ public async Task> StageBlockAsy /// The SHA-256 hash of the provided encryption key. Must be provided if the x-ms-encryption-key header is provided. /// The algorithm used to produce the encryption key hash. Currently, the only accepted value is "AES256". Must be provided if the x-ms-encryption-key header is provided. /// Optional. Version 2019-07-07 and later. Specifies the name of the encryption scope to use to encrypt the data provided in the request. If not specified, encryption is performed with the default account encryption scope. For more information, see Encryption at Rest for Azure Storage Services. + /// Required if the request body is a structured message. Specifies the message schema version and properties. + /// Required if the request body is a structured message. Specifies the length of the blob/file content inside the message body. Will always be smaller than Content-Length. /// The cancellation token to use. /// or is null. - public ResponseWithHeaders StageBlock(string blockId, long contentLength, Stream body, byte[] transactionalContentMD5 = null, byte[] transactionalContentCrc64 = null, int? timeout = null, string leaseId = null, string encryptionKey = null, string encryptionKeySha256 = null, EncryptionAlgorithmTypeInternal? encryptionAlgorithm = null, string encryptionScope = null, CancellationToken cancellationToken = default) + public ResponseWithHeaders StageBlock(string blockId, long contentLength, Stream body, byte[] transactionalContentMD5 = null, byte[] transactionalContentCrc64 = null, int? timeout = null, string leaseId = null, string encryptionKey = null, string encryptionKeySha256 = null, EncryptionAlgorithmTypeInternal? encryptionAlgorithm = null, string encryptionScope = null, string structuredBodyType = null, long? structuredContentLength = null, CancellationToken cancellationToken = default) { if (blockId == null) { @@ -606,7 +630,7 @@ public ResponseWithHeaders StageBlock(string blockId throw new ArgumentNullException(nameof(body)); } - using var message = CreateStageBlockRequest(blockId, contentLength, body, transactionalContentMD5, transactionalContentCrc64, timeout, leaseId, encryptionKey, encryptionKeySha256, encryptionAlgorithm, encryptionScope); + using var message = CreateStageBlockRequest(blockId, contentLength, body, transactionalContentMD5, transactionalContentCrc64, timeout, leaseId, encryptionKey, encryptionKeySha256, encryptionAlgorithm, encryptionScope, structuredBodyType, structuredContentLength); _pipeline.Send(message, cancellationToken); var headers = new BlockBlobStageBlockHeaders(message.Response); switch (message.Response.Status) diff --git a/sdk/storage/Azure.Storage.Blobs/src/Generated/BlockBlobStageBlockHeaders.cs b/sdk/storage/Azure.Storage.Blobs/src/Generated/BlockBlobStageBlockHeaders.cs index 7888b27dd7383..b13a3b7d1609a 100644 --- a/sdk/storage/Azure.Storage.Blobs/src/Generated/BlockBlobStageBlockHeaders.cs +++ b/sdk/storage/Azure.Storage.Blobs/src/Generated/BlockBlobStageBlockHeaders.cs @@ -29,5 +29,7 @@ public BlockBlobStageBlockHeaders(Response response) public string EncryptionKeySha256 => _response.Headers.TryGetValue("x-ms-encryption-key-sha256", out string value) ? value : null; /// Returns the name of the encryption scope used to encrypt the blob contents and application metadata. Note that the absence of this header implies use of the default account encryption scope. public string EncryptionScope => _response.Headers.TryGetValue("x-ms-encryption-scope", out string value) ? value : null; + /// Indicates the structured message body was accepted and mirrors back the message schema version and properties. + public string StructuredBodyType => _response.Headers.TryGetValue("x-ms-structured-body", out string value) ? value : null; } } diff --git a/sdk/storage/Azure.Storage.Blobs/src/Generated/BlockBlobUploadHeaders.cs b/sdk/storage/Azure.Storage.Blobs/src/Generated/BlockBlobUploadHeaders.cs index 1cfbd3924fa55..ca024b1fb5d84 100644 --- a/sdk/storage/Azure.Storage.Blobs/src/Generated/BlockBlobUploadHeaders.cs +++ b/sdk/storage/Azure.Storage.Blobs/src/Generated/BlockBlobUploadHeaders.cs @@ -31,5 +31,7 @@ public BlockBlobUploadHeaders(Response response) public string EncryptionKeySha256 => _response.Headers.TryGetValue("x-ms-encryption-key-sha256", out string value) ? value : null; /// Returns the name of the encryption scope used to encrypt the blob contents and application metadata. Note that the absence of this header implies use of the default account encryption scope. public string EncryptionScope => _response.Headers.TryGetValue("x-ms-encryption-scope", out string value) ? value : null; + /// Indicates the structured message body was accepted and mirrors back the message schema version and properties. + public string StructuredBodyType => _response.Headers.TryGetValue("x-ms-structured-body", out string value) ? value : null; } } diff --git a/sdk/storage/Azure.Storage.Blobs/src/Generated/ContainerRestClient.cs b/sdk/storage/Azure.Storage.Blobs/src/Generated/ContainerRestClient.cs index 024bfecd4e90b..9dd20ee7e1811 100644 --- a/sdk/storage/Azure.Storage.Blobs/src/Generated/ContainerRestClient.cs +++ b/sdk/storage/Azure.Storage.Blobs/src/Generated/ContainerRestClient.cs @@ -31,7 +31,7 @@ internal partial class ContainerRestClient /// The handler for diagnostic messaging in the client. /// The HTTP pipeline for sending and receiving REST requests and responses. /// The URL of the service account, container, or blob that is the target of the desired operation. - /// Specifies the version of the operation to use for this request. The default value is "2024-08-04". + /// Specifies the version of the operation to use for this request. The default value is "2025-01-05". /// , , or is null. public ContainerRestClient(ClientDiagnostics clientDiagnostics, HttpPipeline pipeline, string url, string version) { diff --git a/sdk/storage/Azure.Storage.Blobs/src/Generated/Models/BlobErrorCode.cs b/sdk/storage/Azure.Storage.Blobs/src/Generated/Models/BlobErrorCode.cs index e48c18188c686..57e03c5078ef5 100644 --- a/sdk/storage/Azure.Storage.Blobs/src/Generated/Models/BlobErrorCode.cs +++ b/sdk/storage/Azure.Storage.Blobs/src/Generated/Models/BlobErrorCode.cs @@ -135,6 +135,7 @@ public BlobErrorCode(string value) private const string AuthorizationPermissionMismatchValue = "AuthorizationPermissionMismatch"; private const string AuthorizationServiceMismatchValue = "AuthorizationServiceMismatch"; private const string AuthorizationResourceTypeMismatchValue = "AuthorizationResourceTypeMismatch"; + private const string BlobAccessTierNotSupportedForAccountTypeValue = "BlobAccessTierNotSupportedForAccountType"; /// AccountAlreadyExists. public static BlobErrorCode AccountAlreadyExists { get; } = new BlobErrorCode(AccountAlreadyExistsValue); @@ -362,6 +363,8 @@ public BlobErrorCode(string value) public static BlobErrorCode AuthorizationServiceMismatch { get; } = new BlobErrorCode(AuthorizationServiceMismatchValue); /// AuthorizationResourceTypeMismatch. public static BlobErrorCode AuthorizationResourceTypeMismatch { get; } = new BlobErrorCode(AuthorizationResourceTypeMismatchValue); + /// BlobAccessTierNotSupportedForAccountType. + public static BlobErrorCode BlobAccessTierNotSupportedForAccountType { get; } = new BlobErrorCode(BlobAccessTierNotSupportedForAccountTypeValue); /// Determines if two values are the same. public static bool operator ==(BlobErrorCode left, BlobErrorCode right) => left.Equals(right); /// Determines if two values are not the same. diff --git a/sdk/storage/Azure.Storage.Blobs/src/Generated/PageBlobRestClient.cs b/sdk/storage/Azure.Storage.Blobs/src/Generated/PageBlobRestClient.cs index 260d8021543e2..68a9e85b00d1b 100644 --- a/sdk/storage/Azure.Storage.Blobs/src/Generated/PageBlobRestClient.cs +++ b/sdk/storage/Azure.Storage.Blobs/src/Generated/PageBlobRestClient.cs @@ -30,7 +30,7 @@ internal partial class PageBlobRestClient /// The handler for diagnostic messaging in the client. /// The HTTP pipeline for sending and receiving REST requests and responses. /// The URL of the service account, container, or blob that is the target of the desired operation. - /// Specifies the version of the operation to use for this request. The default value is "2024-08-04". + /// Specifies the version of the operation to use for this request. The default value is "2025-01-05". /// , , or is null. public PageBlobRestClient(ClientDiagnostics clientDiagnostics, HttpPipeline pipeline, string url, string version) { @@ -235,7 +235,7 @@ public ResponseWithHeaders Create(long contentLength, lon } } - internal HttpMessage CreateUploadPagesRequest(long contentLength, Stream body, byte[] transactionalContentMD5, byte[] transactionalContentCrc64, int? timeout, string range, string leaseId, string encryptionKey, string encryptionKeySha256, EncryptionAlgorithmTypeInternal? encryptionAlgorithm, string encryptionScope, long? ifSequenceNumberLessThanOrEqualTo, long? ifSequenceNumberLessThan, long? ifSequenceNumberEqualTo, DateTimeOffset? ifModifiedSince, DateTimeOffset? ifUnmodifiedSince, string ifMatch, string ifNoneMatch, string ifTags) + internal HttpMessage CreateUploadPagesRequest(long contentLength, Stream body, byte[] transactionalContentMD5, byte[] transactionalContentCrc64, int? timeout, string range, string leaseId, string encryptionKey, string encryptionKeySha256, EncryptionAlgorithmTypeInternal? encryptionAlgorithm, string encryptionScope, long? ifSequenceNumberLessThanOrEqualTo, long? ifSequenceNumberLessThan, long? ifSequenceNumberEqualTo, DateTimeOffset? ifModifiedSince, DateTimeOffset? ifUnmodifiedSince, string ifMatch, string ifNoneMatch, string ifTags, string structuredBodyType, long? structuredContentLength) { var message = _pipeline.CreateMessage(); var request = message.Request; @@ -310,6 +310,14 @@ internal HttpMessage CreateUploadPagesRequest(long contentLength, Stream body, b request.Headers.Add("x-ms-if-tags", ifTags); } request.Headers.Add("x-ms-version", _version); + if (structuredBodyType != null) + { + request.Headers.Add("x-ms-structured-body", structuredBodyType); + } + if (structuredContentLength != null) + { + request.Headers.Add("x-ms-structured-content-length", structuredContentLength.Value); + } request.Headers.Add("Accept", "application/xml"); request.Headers.Add("Content-Length", contentLength); if (transactionalContentMD5 != null) @@ -341,16 +349,18 @@ internal HttpMessage CreateUploadPagesRequest(long contentLength, Stream body, b /// Specify an ETag value to operate only on blobs with a matching value. /// Specify an ETag value to operate only on blobs without a matching value. /// Specify a SQL where clause on blob tags to operate only on blobs with a matching value. + /// Required if the request body is a structured message. Specifies the message schema version and properties. + /// Required if the request body is a structured message. Specifies the length of the blob/file content inside the message body. Will always be smaller than Content-Length. /// The cancellation token to use. /// is null. - public async Task> UploadPagesAsync(long contentLength, Stream body, byte[] transactionalContentMD5 = null, byte[] transactionalContentCrc64 = null, int? timeout = null, string range = null, string leaseId = null, string encryptionKey = null, string encryptionKeySha256 = null, EncryptionAlgorithmTypeInternal? encryptionAlgorithm = null, string encryptionScope = null, long? ifSequenceNumberLessThanOrEqualTo = null, long? ifSequenceNumberLessThan = null, long? ifSequenceNumberEqualTo = null, DateTimeOffset? ifModifiedSince = null, DateTimeOffset? ifUnmodifiedSince = null, string ifMatch = null, string ifNoneMatch = null, string ifTags = null, CancellationToken cancellationToken = default) + public async Task> UploadPagesAsync(long contentLength, Stream body, byte[] transactionalContentMD5 = null, byte[] transactionalContentCrc64 = null, int? timeout = null, string range = null, string leaseId = null, string encryptionKey = null, string encryptionKeySha256 = null, EncryptionAlgorithmTypeInternal? encryptionAlgorithm = null, string encryptionScope = null, long? ifSequenceNumberLessThanOrEqualTo = null, long? ifSequenceNumberLessThan = null, long? ifSequenceNumberEqualTo = null, DateTimeOffset? ifModifiedSince = null, DateTimeOffset? ifUnmodifiedSince = null, string ifMatch = null, string ifNoneMatch = null, string ifTags = null, string structuredBodyType = null, long? structuredContentLength = null, CancellationToken cancellationToken = default) { if (body == null) { throw new ArgumentNullException(nameof(body)); } - using var message = CreateUploadPagesRequest(contentLength, body, transactionalContentMD5, transactionalContentCrc64, timeout, range, leaseId, encryptionKey, encryptionKeySha256, encryptionAlgorithm, encryptionScope, ifSequenceNumberLessThanOrEqualTo, ifSequenceNumberLessThan, ifSequenceNumberEqualTo, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, ifTags); + using var message = CreateUploadPagesRequest(contentLength, body, transactionalContentMD5, transactionalContentCrc64, timeout, range, leaseId, encryptionKey, encryptionKeySha256, encryptionAlgorithm, encryptionScope, ifSequenceNumberLessThanOrEqualTo, ifSequenceNumberLessThan, ifSequenceNumberEqualTo, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, ifTags, structuredBodyType, structuredContentLength); await _pipeline.SendAsync(message, cancellationToken).ConfigureAwait(false); var headers = new PageBlobUploadPagesHeaders(message.Response); switch (message.Response.Status) @@ -382,16 +392,18 @@ public async Task> UploadPagesAs /// Specify an ETag value to operate only on blobs with a matching value. /// Specify an ETag value to operate only on blobs without a matching value. /// Specify a SQL where clause on blob tags to operate only on blobs with a matching value. + /// Required if the request body is a structured message. Specifies the message schema version and properties. + /// Required if the request body is a structured message. Specifies the length of the blob/file content inside the message body. Will always be smaller than Content-Length. /// The cancellation token to use. /// is null. - public ResponseWithHeaders UploadPages(long contentLength, Stream body, byte[] transactionalContentMD5 = null, byte[] transactionalContentCrc64 = null, int? timeout = null, string range = null, string leaseId = null, string encryptionKey = null, string encryptionKeySha256 = null, EncryptionAlgorithmTypeInternal? encryptionAlgorithm = null, string encryptionScope = null, long? ifSequenceNumberLessThanOrEqualTo = null, long? ifSequenceNumberLessThan = null, long? ifSequenceNumberEqualTo = null, DateTimeOffset? ifModifiedSince = null, DateTimeOffset? ifUnmodifiedSince = null, string ifMatch = null, string ifNoneMatch = null, string ifTags = null, CancellationToken cancellationToken = default) + public ResponseWithHeaders UploadPages(long contentLength, Stream body, byte[] transactionalContentMD5 = null, byte[] transactionalContentCrc64 = null, int? timeout = null, string range = null, string leaseId = null, string encryptionKey = null, string encryptionKeySha256 = null, EncryptionAlgorithmTypeInternal? encryptionAlgorithm = null, string encryptionScope = null, long? ifSequenceNumberLessThanOrEqualTo = null, long? ifSequenceNumberLessThan = null, long? ifSequenceNumberEqualTo = null, DateTimeOffset? ifModifiedSince = null, DateTimeOffset? ifUnmodifiedSince = null, string ifMatch = null, string ifNoneMatch = null, string ifTags = null, string structuredBodyType = null, long? structuredContentLength = null, CancellationToken cancellationToken = default) { if (body == null) { throw new ArgumentNullException(nameof(body)); } - using var message = CreateUploadPagesRequest(contentLength, body, transactionalContentMD5, transactionalContentCrc64, timeout, range, leaseId, encryptionKey, encryptionKeySha256, encryptionAlgorithm, encryptionScope, ifSequenceNumberLessThanOrEqualTo, ifSequenceNumberLessThan, ifSequenceNumberEqualTo, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, ifTags); + using var message = CreateUploadPagesRequest(contentLength, body, transactionalContentMD5, transactionalContentCrc64, timeout, range, leaseId, encryptionKey, encryptionKeySha256, encryptionAlgorithm, encryptionScope, ifSequenceNumberLessThanOrEqualTo, ifSequenceNumberLessThan, ifSequenceNumberEqualTo, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, ifTags, structuredBodyType, structuredContentLength); _pipeline.Send(message, cancellationToken); var headers = new PageBlobUploadPagesHeaders(message.Response); switch (message.Response.Status) diff --git a/sdk/storage/Azure.Storage.Blobs/src/Generated/PageBlobUploadPagesHeaders.cs b/sdk/storage/Azure.Storage.Blobs/src/Generated/PageBlobUploadPagesHeaders.cs index 77d37d90027aa..c04659bc43322 100644 --- a/sdk/storage/Azure.Storage.Blobs/src/Generated/PageBlobUploadPagesHeaders.cs +++ b/sdk/storage/Azure.Storage.Blobs/src/Generated/PageBlobUploadPagesHeaders.cs @@ -33,5 +33,7 @@ public PageBlobUploadPagesHeaders(Response response) public string EncryptionKeySha256 => _response.Headers.TryGetValue("x-ms-encryption-key-sha256", out string value) ? value : null; /// Returns the name of the encryption scope used to encrypt the blob contents and application metadata. Note that the absence of this header implies use of the default account encryption scope. public string EncryptionScope => _response.Headers.TryGetValue("x-ms-encryption-scope", out string value) ? value : null; + /// Indicates the structured message body was accepted and mirrors back the message schema version and properties. + public string StructuredBodyType => _response.Headers.TryGetValue("x-ms-structured-body", out string value) ? value : null; } } diff --git a/sdk/storage/Azure.Storage.Blobs/src/Generated/ServiceRestClient.cs b/sdk/storage/Azure.Storage.Blobs/src/Generated/ServiceRestClient.cs index e274940f81e8d..2abac369c0cae 100644 --- a/sdk/storage/Azure.Storage.Blobs/src/Generated/ServiceRestClient.cs +++ b/sdk/storage/Azure.Storage.Blobs/src/Generated/ServiceRestClient.cs @@ -31,7 +31,7 @@ internal partial class ServiceRestClient /// The handler for diagnostic messaging in the client. /// The HTTP pipeline for sending and receiving REST requests and responses. /// The URL of the service account, container, or blob that is the target of the desired operation. - /// Specifies the version of the operation to use for this request. The default value is "2024-08-04". + /// Specifies the version of the operation to use for this request. The default value is "2025-01-05". /// , , or is null. public ServiceRestClient(ClientDiagnostics clientDiagnostics, HttpPipeline pipeline, string url, string version) { diff --git a/sdk/storage/Azure.Storage.Blobs/src/autorest.md b/sdk/storage/Azure.Storage.Blobs/src/autorest.md index 85fb92c2349cd..6c18c66066ebd 100644 --- a/sdk/storage/Azure.Storage.Blobs/src/autorest.md +++ b/sdk/storage/Azure.Storage.Blobs/src/autorest.md @@ -4,7 +4,7 @@ Run `dotnet build /t:GenerateCode` to generate code. ``` yaml input-file: - - https://raw.githubusercontent.com/Azure/azure-rest-api-specs/f6f50c6388fd5836fa142384641b8353a99874ef/specification/storage/data-plane/Microsoft.BlobStorage/stable/2024-08-04/blob.json + - https://raw.githubusercontent.com/Azure/azure-rest-api-specs/ae95eb6a4701d844bada7d1c4f5ecf4a7444e5b8/specification/storage/data-plane/Microsoft.BlobStorage/stable/2025-01-05/blob.json generation1-convenience-client: true # https://github.com/Azure/autorest/issues/4075 skip-semantics-validation: true diff --git a/sdk/storage/Azure.Storage.Blobs/tests/BlobBaseClientTests.cs b/sdk/storage/Azure.Storage.Blobs/tests/BlobBaseClientTests.cs index 34aad0482c142..8e51aa708c24b 100644 --- a/sdk/storage/Azure.Storage.Blobs/tests/BlobBaseClientTests.cs +++ b/sdk/storage/Azure.Storage.Blobs/tests/BlobBaseClientTests.cs @@ -7668,6 +7668,605 @@ public async Task GenerateSas_TrimBlobSlashes() } #endregion + #region GenerateUserDelegationSasTests + [RecordedTest] + public async Task GenerateUserDelegationSas_RequiredParameters() + { + // Arrange + TestConstants constants = TestConstants.Create(this); + string containerName = GetNewContainerName(); + string blobName = GetNewBlobName(); + Uri serviceUri = new Uri($"https://{constants.Sas.Account}.blob.core.windows.net"); + BlobUriBuilder blobUriBuilder = new BlobUriBuilder(serviceUri) + { + BlobContainerName = containerName, + BlobName = blobName, + }; + BlobSasPermissions permissions = BlobSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + BlobBaseClient blobClient = InstrumentClient(new BlobBaseClient( + blobUriBuilder.ToUri(), + GetOptions())); + + string stringToSign = null; + Response userDelegationKeyResponse = await GetServiceClient_OAuth().GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: Recording.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + + // Act + Uri sasUri = blobClient.GenerateUserDelegationSasUri(permissions, expiresOn, userDelegationKey, out stringToSign); + + // Assert + BlobSasBuilder sasBuilder = new BlobSasBuilder(permissions, expiresOn) + { + BlobContainerName = containerName, + BlobName = blobName + }; + BlobUriBuilder expectedUri = new BlobUriBuilder(serviceUri) + { + BlobContainerName = containerName, + BlobName = blobName, + Sas = sasBuilder.ToSasQueryParameters(userDelegationKey, blobClient.AccountName) + }; + Assert.AreEqual(expectedUri.ToUri(), sasUri); + Assert.IsNotNull(stringToSign); + } + + [RecordedTest] + public async Task GenerateUserDelegationSas_Builder() + { + TestConstants constants = TestConstants.Create(this); + string containerName = GetNewContainerName(); + string blobName = GetNewBlobName(); + Uri serviceUri = new Uri($"https://{constants.Sas.Account}.blob.core.windows.net"); + BlobUriBuilder blobUriBuilder = new BlobUriBuilder(serviceUri) + { + BlobContainerName = containerName, + BlobName = blobName, + }; + BlobSasPermissions permissions = BlobSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + DateTimeOffset startsOn = Recording.UtcNow.AddHours(-1); + BlobBaseClient blobClient = InstrumentClient(new BlobBaseClient( + blobUriBuilder.ToUri(), + GetOptions())); + + BlobSasBuilder sasBuilder = new BlobSasBuilder(permissions, expiresOn) + { + BlobContainerName = containerName, + BlobName = blobName, + StartsOn = startsOn + }; + + string stringToSign = null; + Response userDelegationKeyResponse = await GetServiceClient_OAuth().GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: Recording.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + + // Act + Uri sasUri = blobClient.GenerateUserDelegationSasUri(sasBuilder, userDelegationKey, out stringToSign); + + // Assert + BlobSasBuilder sasBuilder2 = new BlobSasBuilder(permissions, expiresOn) + { + BlobContainerName = containerName, + BlobName = blobName, + StartsOn = startsOn + }; + BlobUriBuilder expectedUri = new BlobUriBuilder(serviceUri) + { + BlobContainerName = containerName, + BlobName = blobName, + Sas = sasBuilder2.ToSasQueryParameters(userDelegationKey, blobClient.AccountName) + }; + Assert.AreEqual(expectedUri.ToUri(), sasUri); + Assert.IsNotNull(stringToSign); + } + + [RecordedTest] + public async Task GenerateUserDelegationSas_BuilderNull() + { + TestConstants constants = TestConstants.Create(this); + string containerName = GetNewContainerName(); + string blobName = GetNewBlobName(); + Uri serviceUri = new Uri($"https://{constants.Sas.Account}.blob.core.windows.net"); + BlobUriBuilder blobUriBuilder = new BlobUriBuilder(serviceUri) + { + BlobContainerName = containerName, + BlobName = blobName, + }; + BlobBaseClient blobClient = InstrumentClient(new BlobBaseClient( + blobUriBuilder.ToUri(), + GetOptions())); + + string stringToSign = null; + Response userDelegationKeyResponse = await GetServiceClient_OAuth().GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: Recording.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + + // Act + TestHelper.AssertExpectedException( + () => blobClient.GenerateUserDelegationSasUri(null, userDelegationKey, out stringToSign), + new ArgumentNullException("builder")); + } + + [RecordedTest] + public void GenerateUserDelegationSas_UserDelegationKeyNull() + { + TestConstants constants = TestConstants.Create(this); + string containerName = GetNewContainerName(); + string blobName = GetNewBlobName(); + Uri serviceUri = new Uri($"https://{constants.Sas.Account}.blob.core.windows.net"); + BlobUriBuilder blobUriBuilder = new BlobUriBuilder(serviceUri) + { + BlobContainerName = containerName, + BlobName = blobName, + }; + BlobSasPermissions permissions = BlobSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + DateTimeOffset startsOn = Recording.UtcNow.AddHours(-1); + BlobBaseClient blobClient = InstrumentClient(new BlobBaseClient( + blobUriBuilder.ToUri(), + GetOptions())); + + BlobSasBuilder sasBuilder = new BlobSasBuilder(permissions, expiresOn) + { + BlobContainerName = containerName, + BlobName = blobName, + StartsOn = startsOn + }; + + string stringToSign = null; + + // Act + TestHelper.AssertExpectedException( + () => blobClient.GenerateUserDelegationSasUri(sasBuilder, null, out stringToSign), + new ArgumentNullException("userDelegationKey")); + } + + [RecordedTest] + public async Task GenerateUserDelegationSas_BuilderNullContainerName() + { + // Arrange + TestConstants constants = TestConstants.Create(this); + string blobName = GetNewBlobName(); + string containerName = GetNewContainerName(); + Uri serviceUri = new Uri($"https://{constants.Sas.Account}.blob.core.windows.net"); + BlobUriBuilder blobUriBuilder = new BlobUriBuilder(serviceUri) + { + BlobContainerName = containerName, + BlobName = blobName, + }; + BlobSasPermissions permissions = BlobSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + BlobBaseClient blobClient = InstrumentClient(new BlobBaseClient( + blobUriBuilder.ToUri(), + GetOptions())); + + BlobSasBuilder sasBuilder = new BlobSasBuilder(permissions, expiresOn) + { + BlobContainerName = null, + BlobName = blobName, + Resource = "b" + }; + + string stringToSign = null; + Response userDelegationKeyResponse = await GetServiceClient_OAuth().GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: Recording.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + + // Act + Uri sasUri = blobClient.GenerateUserDelegationSasUri(sasBuilder, userDelegationKey, out stringToSign); + + // Assert + BlobSasBuilder sasBuilder2 = new BlobSasBuilder(permissions, expiresOn) + { + BlobContainerName = containerName, + BlobName = blobName, + }; + BlobUriBuilder expectedUri = new BlobUriBuilder(serviceUri) + { + BlobContainerName = containerName, + BlobName = blobName, + Sas = sasBuilder2.ToSasQueryParameters(userDelegationKey, blobClient.AccountName) + }; + Assert.AreEqual(expectedUri.ToUri(), sasUri); + Assert.IsNotNull(stringToSign); + } + + [RecordedTest] + public async Task GenerateUserDelegationSas_BuilderWrongContainerName() + { + // Arrange + TestConstants constants = TestConstants.Create(this); + string blobName = GetNewBlobName(); + string containerName = GetNewContainerName(); + BlobUriBuilder blobUriBuilder = new BlobUriBuilder(new Uri($"https://{constants.Sas.Account}.blob.core.windows.net")) + { + BlobContainerName = containerName, + BlobName = blobName, + }; + BlobSasPermissions permissions = BlobSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + BlobBaseClient blobClient = InstrumentClient(new BlobBaseClient( + blobUriBuilder.ToUri(), + GetOptions())); + + BlobSasBuilder sasBuilder = new BlobSasBuilder(permissions, expiresOn) + { + BlobContainerName = GetNewContainerName(), // set a different containerName + BlobName = blobName, + Resource = "b" + }; + + string stringToSign = null; + Response userDelegationKeyResponse = await GetServiceClient_OAuth().GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: Recording.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + + // Act + TestHelper.AssertExpectedException( + () => blobClient.GenerateUserDelegationSasUri(sasBuilder, userDelegationKey, out stringToSign), + new InvalidOperationException("SAS Uri cannot be generated. BlobSasBuilder.BlobContainerName does not match BlobContainerName in the Client. BlobSasBuilder.BlobContainerName must either be left empty or match the BlobContainerName in the Client")); + } + + [RecordedTest] + public async Task GenerateUserDelegationSas_BuilderNullBlobName() + { + // Arrange + TestConstants constants = TestConstants.Create(this); + string blobName = GetNewBlobName(); + string containerName = GetNewContainerName(); + Uri serviceUri = new Uri($"https://{constants.Sas.Account}.blob.core.windows.net"); + BlobUriBuilder blobUriBuilder = new BlobUriBuilder(serviceUri) + { + BlobContainerName = containerName, + BlobName = blobName, + }; + BlobSasPermissions permissions = BlobSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + DateTimeOffset startsOn = Recording.UtcNow.AddHours(-1); + BlobBaseClient blobClient = InstrumentClient(new BlobBaseClient( + blobUriBuilder.ToUri(), + GetOptions())); + + BlobSasBuilder sasBuilder = new BlobSasBuilder(permissions, expiresOn) + { + BlobContainerName = containerName, + BlobName = null, + Resource = "b", + StartsOn = startsOn + }; + + string stringToSign = null; + Response userDelegationKeyResponse = await GetServiceClient_OAuth().GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: Recording.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + + // Act + Uri sasUri = blobClient.GenerateUserDelegationSasUri(sasBuilder, userDelegationKey, out stringToSign); + + // Assert + BlobSasBuilder sasBuilder2 = new BlobSasBuilder(permissions, expiresOn) + { + BlobContainerName = containerName, + BlobName = blobName, + StartsOn = startsOn + }; + BlobUriBuilder expectedUri = new BlobUriBuilder(serviceUri) + { + BlobContainerName = containerName, + BlobName = blobName, + Sas = sasBuilder2.ToSasQueryParameters(userDelegationKey, blobClient.AccountName) + }; + Assert.AreEqual(expectedUri.ToUri(), sasUri); + Assert.IsNotNull(stringToSign); + } + + [RecordedTest] + public async Task GenerateUserDelegationSas_BuilderWrongBlobName() + { + // Arrange + TestConstants constants = TestConstants.Create(this); + string containerName = GetNewContainerName(); + BlobUriBuilder blobUriBuilder = new BlobUriBuilder(new Uri($"https://{constants.Sas.Account}.blob.core.windows.net")) + { + BlobContainerName = containerName, + }; + BlobSasPermissions permissions = BlobSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + BlobBaseClient blobClient = InstrumentClient(new BlobBaseClient( + blobUriBuilder.ToUri(), + GetOptions())); + + BlobSasBuilder sasBuilder = new BlobSasBuilder(permissions, expiresOn) + { + BlobContainerName = containerName, + BlobName = GetNewBlobName(), // set a different blobName + Resource = "b" + }; + + string stringToSign = null; + Response userDelegationKeyResponse = await GetServiceClient_OAuth().GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: Recording.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + + // Act + TestHelper.AssertExpectedException( + () => blobClient.GenerateUserDelegationSasUri(sasBuilder, userDelegationKey, out stringToSign), + new InvalidOperationException("SAS Uri cannot be generated. BlobSasBuilder.BlobName does not match Name in the Client. BlobSasBuilder.BlobName must either be left empty or match the Name in the Client")); + } + + [RecordedTest] + public async Task GenerateUserDelegationSas_BuilderNullSnapshot() + { + // Arrange + TestConstants constants = TestConstants.Create(this); + string blobName = GetNewBlobName(); + string containerName = GetNewContainerName(); + string snapshot = "2020-07-03T12:45:46.1234567Z"; + Uri serviceUri = new Uri($"https://{constants.Sas.Account}.blob.core.windows.net"); + BlobUriBuilder blobUriBuilder = new BlobUriBuilder(serviceUri) + { + BlobContainerName = containerName, + BlobName = blobName, + Snapshot = snapshot + }; + BlobSasPermissions permissions = BlobSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + DateTimeOffset startsOn = Recording.UtcNow.AddHours(-1); + BlobBaseClient blobClient = InstrumentClient(new BlobBaseClient( + blobUriBuilder.ToUri(), + GetOptions())); + + BlobSasBuilder sasBuilder = new BlobSasBuilder(permissions, expiresOn) + { + BlobContainerName = containerName, + BlobName = blobName, + Snapshot = null, + Resource = "b", + StartsOn = startsOn + }; + + string stringToSign = null; + Response userDelegationKeyResponse = await GetServiceClient_OAuth().GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: Recording.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + + // Act + Uri sasUri = blobClient.GenerateUserDelegationSasUri(sasBuilder, userDelegationKey, out stringToSign); + + // Assert + BlobSasBuilder sasBuilder2 = new BlobSasBuilder(permissions, expiresOn) + { + BlobContainerName = containerName, + BlobName = blobName, + Snapshot = snapshot, + StartsOn = startsOn + }; + BlobUriBuilder expectedUri = new BlobUriBuilder(serviceUri) + { + BlobContainerName = containerName, + BlobName = blobName, + Snapshot = snapshot, + Sas = sasBuilder2.ToSasQueryParameters(userDelegationKey, blobClient.AccountName) + }; + Assert.AreEqual(expectedUri.ToUri(), sasUri); + Assert.IsNotNull(stringToSign); + } + + [RecordedTest] + public async Task GenerateUserDelegationSas_BuilderWrongSnapshot() + { + // Arrange + TestConstants constants = TestConstants.Create(this); + string snapshot = "2020-07-03T12:45:46.1234567Z"; + string differentSnapshot = "2019-07-03T12:45:46.1234567Z"; + string containerName = GetNewContainerName(); + string blobName = GetNewBlobName(); + BlobUriBuilder blobUriBuilder = new BlobUriBuilder(new Uri($"https://{constants.Sas.Account}.blob.core.windows.net")) + { + BlobContainerName = containerName, + BlobName = blobName, + Snapshot = snapshot + }; + BlobSasPermissions permissions = BlobSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + BlobBaseClient blobClient = InstrumentClient(new BlobBaseClient( + blobUriBuilder.ToUri(), + GetOptions())); + + BlobSasBuilder sasBuilder = new BlobSasBuilder(permissions, expiresOn) + { + BlobContainerName = containerName, + BlobName = blobName, + Resource = "bs", + Snapshot = differentSnapshot + }; + + string stringToSign = null; + Response userDelegationKeyResponse = await GetServiceClient_OAuth().GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: Recording.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + + // Act + TestHelper.AssertExpectedException( + () => blobClient.GenerateUserDelegationSasUri(sasBuilder, userDelegationKey, out stringToSign), + new InvalidOperationException("SAS Uri cannot be generated. BlobSasBuilder.Snapshot does not match snapshot value in the URI in the Client. BlobSasBuilder.Snapshot must either be left empty or match the snapshot value in the URI in the Client")); + } + + [RecordedTest] + public async Task GenerateUserDelegationSas_BuilderNullVersion() + { + // Arrange + TestConstants constants = TestConstants.Create(this); + string blobName = GetNewBlobName(); + string containerName = GetNewContainerName(); + string versionId = "2020-07-03T12:45:46.1234567Z"; + Uri serviceUri = new Uri($"https://{constants.Sas.Account}.blob.core.windows.net"); + BlobUriBuilder blobUriBuilder = new BlobUriBuilder(serviceUri) + { + BlobContainerName = containerName, + BlobName = blobName, + VersionId = versionId + }; + BlobSasPermissions permissions = BlobSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + DateTimeOffset startsOn = Recording.UtcNow.AddHours(-1); + BlobBaseClient blobClient = InstrumentClient(new BlobBaseClient( + blobUriBuilder.ToUri(), + GetOptions())); + + BlobSasBuilder sasBuilder = new BlobSasBuilder(permissions, expiresOn) + { + BlobContainerName = containerName, + BlobName = blobName, + BlobVersionId = null, + Resource = "b", + StartsOn = startsOn + }; + + string stringToSign = null; + Response userDelegationKeyResponse = await GetServiceClient_OAuth().GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: Recording.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + + // Act + Uri sasUri = blobClient.GenerateUserDelegationSasUri(sasBuilder, userDelegationKey, out stringToSign); + + // Assert + BlobSasBuilder sasBuilder2 = new BlobSasBuilder(permissions, expiresOn) + { + BlobContainerName = containerName, + BlobName = blobName, + BlobVersionId = versionId, + StartsOn = startsOn + }; + BlobUriBuilder expectedUri = new BlobUriBuilder(serviceUri) + { + BlobContainerName = containerName, + BlobName = blobName, + VersionId = versionId, + Sas = sasBuilder2.ToSasQueryParameters(userDelegationKey, blobClient.AccountName) + }; + Assert.AreEqual(expectedUri.ToUri(), sasUri); + Assert.IsNotNull(stringToSign); + } + + [RecordedTest] + public async Task GenerateUserDelegationSas_BuilderWrongVersion() + { + // Arrange + TestConstants constants = TestConstants.Create(this); + string blobVersionId = "2020-07-03T12:45:46.1234567Z"; + string diffBlobVersionId = "2019-07-03T12:45:46.1234567Z"; + string containerName = GetNewContainerName(); + string blobName = GetNewBlobName(); + Uri blobEndpoint = new Uri($"https://{constants.Sas.Account}.blob.core.windows.net"); + BlobUriBuilder blobUriBuilder = new BlobUriBuilder(blobEndpoint) + { + BlobContainerName = containerName, + BlobName = blobName, + VersionId = blobVersionId + }; + + BlobSasPermissions permissions = BlobSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + BlobBaseClient blobClient = InstrumentClient(new BlobBaseClient( + blobUriBuilder.ToUri(), + GetOptions())); + + BlobSasBuilder sasBuilder = new BlobSasBuilder(permissions, expiresOn) + { + BlobContainerName = containerName, + BlobName = blobName, + Resource = "bs", + BlobVersionId = diffBlobVersionId, + }; + + string stringToSign = null; + Response userDelegationKeyResponse = await GetServiceClient_OAuth().GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: Recording.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + + // Act + TestHelper.AssertExpectedException( + () => blobClient.GenerateUserDelegationSasUri(sasBuilder, userDelegationKey, out stringToSign), + new InvalidOperationException("SAS Uri cannot be generated. BlobSasBuilder.BlobVersionId does not match snapshot value in the URI in the Client. BlobSasBuilder.BlobVersionId must either be left empty or match the snapshot value in the URI in the Client")); + } + + [LiveOnly] + public async Task GenerateUserDelegationSas_TrimBlobSlashes() + { + // Arrange + BlobServiceClient serviceClient = GetServiceClient_OAuth(); + await using DisposingContainer test = await GetTestContainerAsync( + service: serviceClient); + string containerName = test.Container.Name; + string blobName = $"/{GetNewBlobName()}"; + + BlobUriBuilder blobUriBuilder = new BlobUriBuilder(test.Container.Uri, false) + { + BlobContainerName = containerName, + BlobName = blobName, + }; + + // Set up options with TrimBlobNameSlashes set to false + BlobClientOptions options = GetOptions(); + options.TrimBlobNameSlashes = false; + BlobSasPermissions permissions = BlobSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + AppendBlobClient createClient = InstrumentClient(new AppendBlobClient( + blobUriBuilder.ToUri(), + TestEnvironment.Credential, + options)); + + await createClient.CreateAsync(); + + string stringToSign = null; + Response userDelegationKeyResponse = await serviceClient.GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: Recording.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + + // Act + BlobBaseClient blobClient = InstrumentClient(new BlobBaseClient( + blobUriBuilder.ToUri(), + options)); + + Uri sasUri = blobClient.GenerateUserDelegationSasUri(permissions, expiresOn, userDelegationKey, out stringToSign); + + // Assert + BlobSasBuilder sasBuilder = new BlobSasBuilder(permissions, expiresOn) + { + BlobContainerName = containerName, + BlobName = blobName + }; + BlobUriBuilder expectedUri = new BlobUriBuilder(test.Container.Uri, false) + { + BlobContainerName = containerName, + BlobName = blobName, + Sas = sasBuilder.ToSasQueryParameters(userDelegationKey, blobClient.AccountName) + }; + Assert.AreEqual(expectedUri.ToUri(), sasUri); + Assert.IsNotNull(stringToSign); + + BlobBaseClient sasClient = InstrumentClient(new BlobBaseClient(sasUri, options)); + Assert.IsTrue(await sasClient.ExistsAsync()); + } + #endregion + [Test] [TestCase(null, false)] [TestCase("ContainerNotFound", true)] diff --git a/sdk/storage/Azure.Storage.Blobs/tests/BlobsClientTestFixtureAttribute.cs b/sdk/storage/Azure.Storage.Blobs/tests/BlobsClientTestFixtureAttribute.cs index bb82aeae55ff2..d0372ab20cf47 100644 --- a/sdk/storage/Azure.Storage.Blobs/tests/BlobsClientTestFixtureAttribute.cs +++ b/sdk/storage/Azure.Storage.Blobs/tests/BlobsClientTestFixtureAttribute.cs @@ -35,6 +35,7 @@ public BlobsClientTestFixtureAttribute(params object[] additionalParameters) BlobClientOptions.ServiceVersion.V2024_05_04, BlobClientOptions.ServiceVersion.V2024_08_04, BlobClientOptions.ServiceVersion.V2024_11_04, + BlobClientOptions.ServiceVersion.V2025_01_05, StorageVersionExtensions.LatestVersion, StorageVersionExtensions.MaxVersion }, diff --git a/sdk/storage/Azure.Storage.Blobs/tests/ContainerClientTests.cs b/sdk/storage/Azure.Storage.Blobs/tests/ContainerClientTests.cs index f1e69e70bb4e5..451586836669f 100644 --- a/sdk/storage/Azure.Storage.Blobs/tests/ContainerClientTests.cs +++ b/sdk/storage/Azure.Storage.Blobs/tests/ContainerClientTests.cs @@ -3659,6 +3659,269 @@ public void GenerateSas_BuilderWrongName() } #endregion + #region GenerateUserDelegationSas + [RecordedTest] + public async Task GenerateUserDelegationSas_RequiredParameters() + { + // Arrange + TestConstants constants = TestConstants.Create(this); + string containerName = GetNewContainerName(); + Uri serviceUri = new Uri($"https://{constants.Sas.Account}.blob.core.windows.net"); + BlobUriBuilder blobUriBuilder = new BlobUriBuilder(serviceUri) + { + BlobContainerName = containerName, + }; + BlobContainerSasPermissions permissions = BlobContainerSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + BlobContainerClient containerClient = InstrumentClient( + new BlobContainerClient( + blobUriBuilder.ToUri(), + GetOptions())); + + string stringToSign = null; + Response userDelegationKeyResponse = await GetServiceClient_OAuth().GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: Recording.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + + //Act + Uri sasUri = containerClient.GenerateUserDelegationSasUri(permissions, expiresOn, userDelegationKey, out stringToSign); + + // Assert + BlobSasBuilder sasBuilder = new BlobSasBuilder(permissions, expiresOn) + { + BlobContainerName = containerName, + }; + BlobUriBuilder expectedUri = new BlobUriBuilder(serviceUri) + { + BlobContainerName = containerName, + Sas = sasBuilder.ToSasQueryParameters(userDelegationKey, containerClient.AccountName) + }; + Assert.AreEqual(expectedUri.ToUri(), sasUri); + Assert.IsNotNull(stringToSign); + } + + [RecordedTest] + public async Task GenerateUserDelegationSas_Builder() + { + TestConstants constants = TestConstants.Create(this); + string containerName = GetNewContainerName(); + Uri serviceUri = new Uri($"https://{constants.Sas.Account}.blob.core.windows.net"); + BlobUriBuilder blobUriBuilder = new BlobUriBuilder(serviceUri) + { + BlobContainerName = containerName + }; + BlobContainerSasPermissions permissions = BlobContainerSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + BlobContainerClient containerClient = + InstrumentClient(new BlobContainerClient( + blobUriBuilder.ToUri(), + GetOptions())); + + BlobSasBuilder sasBuilder = new BlobSasBuilder(permissions, expiresOn) + { + BlobContainerName = containerName, + }; + + string stringToSign = null; + Response userDelegationKeyResponse = await GetServiceClient_OAuth().GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: Recording.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + + // Act + Uri sasUri = containerClient.GenerateUserDelegationSasUri(sasBuilder, userDelegationKey, out stringToSign); + + // Assert + BlobSasBuilder sasBuilder2 = new BlobSasBuilder(permissions, expiresOn) + { + BlobContainerName = containerName + }; + BlobUriBuilder expectedUri = new BlobUriBuilder(serviceUri) + { + BlobContainerName = containerName, + Sas = sasBuilder2.ToSasQueryParameters(userDelegationKey, containerClient.AccountName) + }; + Assert.AreEqual(expectedUri.ToUri(), sasUri); + Assert.IsNotNull(stringToSign); + } + + [RecordedTest] + public async Task GenerateUserDelegationSas_BuilderNull() + { + TestConstants constants = TestConstants.Create(this); + string containerName = GetNewContainerName(); + Uri serviceUri = new Uri($"https://{constants.Sas.Account}.blob.core.windows.net"); + BlobUriBuilder blobUriBuilder = new BlobUriBuilder(serviceUri) + { + BlobContainerName = containerName + }; + BlobContainerClient containerClient = + InstrumentClient(new BlobContainerClient( + blobUriBuilder.ToUri(), + GetOptions())); + + string stringToSign = null; + Response userDelegationKeyResponse = await GetServiceClient_OAuth().GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: Recording.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + + // Act + TestHelper.AssertExpectedException( + () => containerClient.GenerateUserDelegationSasUri(null, userDelegationKey, out stringToSign), + new ArgumentNullException("builder")); + } + + [RecordedTest] + public void GenerateUserDelegationSas_UserDelegationKeyNull() + { + TestConstants constants = TestConstants.Create(this); + string containerName = GetNewContainerName(); + Uri serviceUri = new Uri($"https://{constants.Sas.Account}.blob.core.windows.net"); + BlobUriBuilder blobUriBuilder = new BlobUriBuilder(serviceUri) + { + BlobContainerName = containerName + }; + BlobContainerSasPermissions permissions = BlobContainerSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + BlobContainerClient containerClient = + InstrumentClient(new BlobContainerClient( + blobUriBuilder.ToUri(), + GetOptions())); + + BlobSasBuilder sasBuilder = new BlobSasBuilder(permissions, expiresOn) + { + BlobContainerName = containerName + }; + + string stringToSign = null; + + // Act + TestHelper.AssertExpectedException( + () => containerClient.GenerateUserDelegationSasUri(sasBuilder, null, out stringToSign), + new ArgumentNullException("userDelegationKey")); + } + + [RecordedTest] + public async Task GenerateUserDelegationSas_BuilderNullName() + { + TestConstants constants = TestConstants.Create(this); + string containerName = GetNewContainerName(); + Uri serviceUri = new Uri($"https://{constants.Sas.Account}.blob.core.windows.net"); + BlobUriBuilder blobUriBuilder = new BlobUriBuilder(serviceUri) + { + BlobContainerName = containerName + }; + BlobContainerSasPermissions permissions = BlobContainerSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + BlobContainerClient containerClient = + InstrumentClient(new BlobContainerClient( + blobUriBuilder.ToUri(), + GetOptions())); + + BlobSasBuilder sasBuilder = new BlobSasBuilder(permissions, expiresOn) + { + BlobContainerName = null + }; + + string stringToSign = null; + Response userDelegationKeyResponse = await GetServiceClient_OAuth().GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: Recording.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + + // Act + Uri sasUri = containerClient.GenerateUserDelegationSasUri(sasBuilder, userDelegationKey, out stringToSign); + + // Assert + BlobSasBuilder sasBuilder2 = new BlobSasBuilder(permissions, expiresOn) + { + BlobContainerName = containerName + }; + BlobUriBuilder expectedUri = new BlobUriBuilder(serviceUri) + { + BlobContainerName = containerName, + Sas = sasBuilder2.ToSasQueryParameters(userDelegationKey, containerClient.AccountName) + }; + + Assert.AreEqual(expectedUri.ToUri(), sasUri); + Assert.IsNotNull(stringToSign); + } + + [RecordedTest] + public async Task GenerateUserDelegationSas_BuilderWrongName() + { + // Arrange + TestConstants constants = TestConstants.Create(this); + string containerName = GetNewContainerName(); + Uri serviceUri = new Uri($"https://{constants.Sas.Account}.blob.core.windows.net"); + BlobUriBuilder blobUriBuilder = new BlobUriBuilder(serviceUri) + { + BlobContainerName = containerName + }; + BlobSasPermissions permissions = BlobSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + BlobContainerClient containerClient = InstrumentClient(new BlobContainerClient( + blobUriBuilder.ToUri(), + GetOptions())); + + BlobSasBuilder sasBuilder = new BlobSasBuilder(permissions, expiresOn) + { + BlobContainerName = GetNewContainerName(), // set a different containerName + Resource = "b" + }; + + string stringToSign = null; + Response userDelegationKeyResponse = await GetServiceClient_OAuth().GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: Recording.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + + // Act + TestHelper.AssertExpectedException( + () => containerClient.GenerateUserDelegationSasUri(sasBuilder, userDelegationKey, out stringToSign), + new InvalidOperationException("SAS Uri cannot be generated. BlobSasBuilder.BlobContainerName does not match Name in the Client. BlobSasBuilder.BlobContainerName must either be left empty or match the Name in the Client")); + } + + [RecordedTest] + public async Task GenerateUserDelegationSas_BuilderIncorrectlySettingBlobName() + { + // Arrange + TestConstants constants = TestConstants.Create(this); + string containerName = GetNewContainerName(); + string blobName = GetNewBlobName(); + Uri serviceUri = new Uri($"https://{constants.Sas.Account}.blob.core.windows.net"); + BlobUriBuilder blobUriBuilder = new BlobUriBuilder(serviceUri) + { + BlobContainerName = containerName, + BlobName = blobName + }; + BlobSasPermissions permissions = BlobSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + BlobContainerClient containerClient = InstrumentClient(new BlobContainerClient( + blobUriBuilder.ToUri(), + GetOptions())); + + BlobSasBuilder sasBuilder = new BlobSasBuilder(permissions, expiresOn) + { + BlobContainerName = containerName, + BlobName = blobName + }; + + string stringToSign = null; + Response userDelegationKeyResponse = await GetServiceClient_OAuth().GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: Recording.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + + // Act + TestHelper.AssertExpectedException( + () => containerClient.GenerateUserDelegationSasUri(sasBuilder, userDelegationKey, out stringToSign), + new InvalidOperationException("SAS Uri cannot be generated. builder.BlobName cannot be set to create a Name SAS.")); + } + #endregion + [RecordedTest] public void CanMockBlobClientsRetrieval() { diff --git a/sdk/storage/Azure.Storage.Blobs/tests/ImmutableStorageWithVersioningTests.cs b/sdk/storage/Azure.Storage.Blobs/tests/ImmutableStorageWithVersioningTests.cs index 3eac647db9cf8..af473b946047f 100644 --- a/sdk/storage/Azure.Storage.Blobs/tests/ImmutableStorageWithVersioningTests.cs +++ b/sdk/storage/Azure.Storage.Blobs/tests/ImmutableStorageWithVersioningTests.cs @@ -562,6 +562,92 @@ public async Task DeleteImmutibilityPolicyAsync() Assert.IsNull(propertiesResponse.Value.ImmutabilityPolicy.PolicyMode); } + [Test] + [ServiceVersion(Min = BlobClientOptions.ServiceVersion.V2020_06_12)] + public async Task SetDeleteImmutibilityPolicyAsync_Snapshot() + { + // Arrange + BlobBaseClient blob = await GetNewBlobClient(_containerClient); + + Response createSnapshotResponse = await blob.CreateSnapshotAsync(); + BlobBaseClient snapshotClient = blob.WithSnapshot(createSnapshotResponse.Value.Snapshot); + try + { + BlobImmutabilityPolicy immutabilityPolicy = new BlobImmutabilityPolicy + { + ExpiresOn = Recording.UtcNow.AddSeconds(5), + PolicyMode = BlobImmutabilityPolicyMode.Unlocked + }; + + // Act + await snapshotClient.SetImmutabilityPolicyAsync(immutabilityPolicy); + + // Assert that the base blob does not have an immutability policy. + Response propertiesResponse = await blob.GetPropertiesAsync(); + Assert.IsNull(propertiesResponse.Value.ImmutabilityPolicy.ExpiresOn); + Assert.IsNull(propertiesResponse.Value.ImmutabilityPolicy.PolicyMode); + + // Assert that the blob snapshot has an immuability policy. + propertiesResponse = await snapshotClient.GetPropertiesAsync(); + Assert.IsNotNull(propertiesResponse.Value.ImmutabilityPolicy.ExpiresOn); + Assert.IsNotNull(propertiesResponse.Value.ImmutabilityPolicy.PolicyMode); + + await snapshotClient.DeleteImmutabilityPolicyAsync(); + + // Assert + propertiesResponse = await snapshotClient.GetPropertiesAsync(); + Assert.IsNull(propertiesResponse.Value.ImmutabilityPolicy.ExpiresOn); + Assert.IsNull(propertiesResponse.Value.ImmutabilityPolicy.PolicyMode); + } + finally + { + await snapshotClient.DeleteAsync(); + } + } + + [Test] + [ServiceVersion(Min = BlobClientOptions.ServiceVersion.V2020_06_12)] + public async Task SetDeleteImmutibilityPolicyAsync_BlobVersion() + { + // Arrange + BlobBaseClient blob = await GetNewBlobClient(_containerClient); + + IDictionary metadata = BuildMetadata(); + + // Create Blob Version + Response setMetadataResponse = await blob.SetMetadataAsync(metadata); + BlobBaseClient versionClient = blob.WithVersion(setMetadataResponse.Value.VersionId); + + // Create another blob Version + await blob.SetMetadataAsync(new Dictionary()); + + BlobImmutabilityPolicy immutabilityPolicy = new BlobImmutabilityPolicy + { + ExpiresOn = Recording.UtcNow.AddSeconds(5), + PolicyMode = BlobImmutabilityPolicyMode.Unlocked + }; + + // Act + await versionClient.SetImmutabilityPolicyAsync(immutabilityPolicy); + + // Assert that the base blob does not have an immutability policy + Response propertiesResponse = await blob.GetPropertiesAsync(); + Assert.IsNull(propertiesResponse.Value.ImmutabilityPolicy.ExpiresOn); + Assert.IsNull(propertiesResponse.Value.ImmutabilityPolicy.PolicyMode); + + // Assert that the blob version does have an immutability policy + propertiesResponse = await versionClient.GetPropertiesAsync(); + Assert.IsNotNull(propertiesResponse.Value.ImmutabilityPolicy.ExpiresOn); + Assert.IsNotNull(propertiesResponse.Value.ImmutabilityPolicy.PolicyMode); + + await versionClient.DeleteImmutabilityPolicyAsync(); + + // Assert blob version does not have an immutability policy + propertiesResponse = await versionClient.GetPropertiesAsync(); + Assert.IsNull(propertiesResponse.Value.ImmutabilityPolicy.ExpiresOn); + Assert.IsNull(propertiesResponse.Value.ImmutabilityPolicy.PolicyMode); + } + [Test] [ServiceVersion(Min = BlobClientOptions.ServiceVersion.V2020_06_12)] public async Task DeleteImmutibilityPolicyAsync_Error() @@ -621,6 +707,67 @@ public async Task SetLegalHoldAsync() Assert.IsFalse(response.Value.HasLegalHold); } + [Test] + [ServiceVersion(Min = BlobClientOptions.ServiceVersion.V2020_06_12)] + public async Task SetLegalHoldAsync_Snapshot() + { + // Arrange + BlobBaseClient blob = await GetNewBlobClient(_containerClient); + + Response createSnapshotResponse = await blob.CreateSnapshotAsync(); + BlobBaseClient snapshotClient = blob.WithSnapshot(createSnapshotResponse.Value.Snapshot); + + try + { + // Act + await snapshotClient.SetLegalHoldAsync(true); + + // Assert the blob snapshot has a legal hold + Response propertiesResponse = await snapshotClient.GetPropertiesAsync(); + Assert.IsTrue(propertiesResponse.Value.HasLegalHold); + + // Assert the base blob does not have a legal hold + propertiesResponse = await blob.GetPropertiesAsync(); + Assert.IsFalse(propertiesResponse.Value.HasLegalHold); + + await snapshotClient.SetLegalHoldAsync(false); + } + finally + { + await snapshotClient.DeleteAsync(); + } + } + + [Test] + [ServiceVersion(Min = BlobClientOptions.ServiceVersion.V2020_06_12)] + public async Task SetLegalHoldAsync_BlobVersion() + { + // Arrange + BlobBaseClient blob = await GetNewBlobClient(_containerClient); + + IDictionary metadata = BuildMetadata(); + + // Create Blob Version + Response setMetadataResponse = await blob.SetMetadataAsync(metadata); + BlobBaseClient versionClient = blob.WithVersion(setMetadataResponse.Value.VersionId); + + // Create another blob Version + await blob.SetMetadataAsync(new Dictionary()); + + // Act + await versionClient.SetLegalHoldAsync(true); + + // Assert the blob version has a legal hold + Response propertiesResponse = await versionClient.GetPropertiesAsync(); + Assert.IsTrue(propertiesResponse.Value.HasLegalHold); + + // Assert the base blob does not have a legal hold + propertiesResponse = await blob.GetPropertiesAsync(); + Assert.IsFalse(propertiesResponse.Value.HasLegalHold); + + await versionClient.SetLegalHoldAsync(false); + } + [Test] [ServiceVersion(Min = BlobClientOptions.ServiceVersion.V2020_06_12)] public async Task SetLegalHoldAsync_Error() diff --git a/sdk/storage/Azure.Storage.Common/CHANGELOG.md b/sdk/storage/Azure.Storage.Common/CHANGELOG.md index b45093ef4fdce..aad2683bdfb5b 100644 --- a/sdk/storage/Azure.Storage.Common/CHANGELOG.md +++ b/sdk/storage/Azure.Storage.Common/CHANGELOG.md @@ -3,12 +3,7 @@ ## 12.22.0-beta.1 (Unreleased) ### Features Added - -### Breaking Changes - -### Bugs Fixed - -### Other Changes +- This release contains bug fixes to improve quality. ## 12.21.0 (2024-09-18) diff --git a/sdk/storage/Azure.Storage.Common/api/Azure.Storage.Common.net6.0.cs b/sdk/storage/Azure.Storage.Common/api/Azure.Storage.Common.net6.0.cs index 39ffce6f73614..121838723ee4f 100644 --- a/sdk/storage/Azure.Storage.Common/api/Azure.Storage.Common.net6.0.cs +++ b/sdk/storage/Azure.Storage.Common/api/Azure.Storage.Common.net6.0.cs @@ -183,7 +183,7 @@ public enum SasProtocol } public partial class SasQueryParameters { - public const string DefaultSasVersion = "2024-11-04"; + public const string DefaultSasVersion = "2025-01-05"; protected SasQueryParameters() { } protected SasQueryParameters(System.Collections.Generic.IDictionary values) { } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] diff --git a/sdk/storage/Azure.Storage.Common/api/Azure.Storage.Common.netstandard2.0.cs b/sdk/storage/Azure.Storage.Common/api/Azure.Storage.Common.netstandard2.0.cs index 55ce1a3aa640e..9b59550e809d0 100644 --- a/sdk/storage/Azure.Storage.Common/api/Azure.Storage.Common.netstandard2.0.cs +++ b/sdk/storage/Azure.Storage.Common/api/Azure.Storage.Common.netstandard2.0.cs @@ -182,7 +182,7 @@ public enum SasProtocol } public partial class SasQueryParameters { - public const string DefaultSasVersion = "2024-11-04"; + public const string DefaultSasVersion = "2025-01-05"; protected SasQueryParameters() { } protected SasQueryParameters(System.Collections.Generic.IDictionary values) { } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] diff --git a/sdk/storage/Azure.Storage.Common/src/Shared/Constants.cs b/sdk/storage/Azure.Storage.Common/src/Shared/Constants.cs index f8ddc5731affb..3e00882188fba 100644 --- a/sdk/storage/Azure.Storage.Common/src/Shared/Constants.cs +++ b/sdk/storage/Azure.Storage.Common/src/Shared/Constants.cs @@ -25,7 +25,7 @@ internal static class Constants /// Gets the default service version to use when building shared access /// signatures. /// - public const string DefaultSasVersion = "2024-11-04"; + public const string DefaultSasVersion = "2025-01-05"; /// /// Max download range size while requesting a transactional hash. diff --git a/sdk/storage/Azure.Storage.Common/src/Shared/Errors.Clients.cs b/sdk/storage/Azure.Storage.Common/src/Shared/Errors.Clients.cs index 4e5464fa17e6e..2a5fe38668104 100644 --- a/sdk/storage/Azure.Storage.Common/src/Shared/Errors.Clients.cs +++ b/sdk/storage/Azure.Storage.Common/src/Shared/Errors.Clients.cs @@ -78,7 +78,7 @@ public static InvalidOperationException SasBuilderEmptyParam(string builderName, => new InvalidOperationException($"SAS Uri cannot be generated. {builderName}.{paramName} cannot be set to create a {sasType} SAS."); public static InvalidOperationException SasIncorrectResourceType(string builderName, string builderParam, string value, string clientName) - => new InvalidOperationException($"SAS Uri cannot be generated. Expected {builderName}.{builderParam} to be set to {value} to generate" + + => new InvalidOperationException($"SAS Uri cannot be generated. Expected {builderName}.{builderParam} to be set to {value} to generate " + $"the respective SAS for the client, {clientName}"); public static ArgumentException InvalidPermission(char s) diff --git a/sdk/storage/Azure.Storage.Common/src/Shared/StorageVersionExtensions.cs b/sdk/storage/Azure.Storage.Common/src/Shared/StorageVersionExtensions.cs index 979dbbcf20ddc..44c0973ea9be1 100644 --- a/sdk/storage/Azure.Storage.Common/src/Shared/StorageVersionExtensions.cs +++ b/sdk/storage/Azure.Storage.Common/src/Shared/StorageVersionExtensions.cs @@ -46,7 +46,7 @@ internal static class StorageVersionExtensions /// public const ServiceVersion LatestVersion = #if BlobSDK || QueueSDK || FileSDK || DataLakeSDK || ChangeFeedSDK || DataMovementSDK || BlobDataMovementSDK || ShareDataMovementSDK - ServiceVersion.V2024_11_04; + ServiceVersion.V2025_01_05; #else ERROR_STORAGE_SERVICE_NOT_DEFINED; #endif @@ -56,7 +56,7 @@ internal static class StorageVersionExtensions /// internal const ServiceVersion MaxVersion = #if BlobSDK || QueueSDK || FileSDK || DataLakeSDK || ChangeFeedSDK || DataMovementSDK || BlobDataMovementSDK || ShareDataMovementSDK - ServiceVersion.V2024_11_04; + ServiceVersion.V2025_01_05; #else ERROR_STORAGE_SERVICE_NOT_DEFINED; #endif @@ -69,32 +69,7 @@ internal static class StorageVersionExtensions public static string ToVersionString(this ServiceVersion version) => version switch { -#if BlobSDK || FileSDK || DataLakeSDK - ServiceVersion.V2019_02_02 => "2019-02-02", - ServiceVersion.V2019_07_07 => "2019-07-07", - ServiceVersion.V2019_12_12 => "2019-12-12", - ServiceVersion.V2020_02_10 => "2020-02-10", - ServiceVersion.V2020_04_08 => "2020-04-08", - ServiceVersion.V2020_06_12 => "2020-06-12", - ServiceVersion.V2020_08_04 => "2020-08-04", - ServiceVersion.V2020_10_02 => "2020-10-02", - ServiceVersion.V2020_12_06 => "2020-12-06", - ServiceVersion.V2021_02_12 => "2021-02-12", - ServiceVersion.V2021_04_10 => "2021-04-10", - ServiceVersion.V2021_06_08 => "2021-06-08", - ServiceVersion.V2021_08_06 => "2021-08-06", - ServiceVersion.V2021_10_04 => "2021-10-04", - ServiceVersion.V2021_12_02 => "2021-12-02", - ServiceVersion.V2022_11_02 => "2022-11-02", - ServiceVersion.V2023_01_03 => "2023-01-03", - ServiceVersion.V2023_05_03 => "2023-05-03", - ServiceVersion.V2023_08_03 => "2023-08-03", - ServiceVersion.V2023_11_03 => "2023-11-03", - ServiceVersion.V2024_02_04 => "2024-02-04", - ServiceVersion.V2024_05_04 => "2024-05-04", - ServiceVersion.V2024_08_04 => "2024-08-04", - ServiceVersion.V2024_11_04 => "2024-11-04", -#elif QueueSDK +#if BlobSDK || FileSDK || DataLakeSDK || QueueSDK ServiceVersion.V2019_02_02 => "2019-02-02", ServiceVersion.V2019_07_07 => "2019-07-07", ServiceVersion.V2019_12_12 => "2019-12-12", @@ -119,6 +94,7 @@ public static string ToVersionString(this ServiceVersion version) => ServiceVersion.V2024_05_04 => "2024-05-04", ServiceVersion.V2024_08_04 => "2024-08-04", ServiceVersion.V2024_11_04 => "2024-11-04", + ServiceVersion.V2025_01_05 => "2025-01-05", #endif _ => throw Errors.VersionNotSupported(nameof(version)) }; @@ -180,6 +156,8 @@ public static Azure.Storage.Blobs.BlobClientOptions.ServiceVersion AsBlobsVersio Azure.Storage.Blobs.BlobClientOptions.ServiceVersion.V2024_08_04, Azure.Storage.Files.DataLake.DataLakeClientOptions.ServiceVersion.V2024_11_04 => Azure.Storage.Blobs.BlobClientOptions.ServiceVersion.V2024_11_04, + Azure.Storage.Files.DataLake.DataLakeClientOptions.ServiceVersion.V2025_01_05 => + Azure.Storage.Blobs.BlobClientOptions.ServiceVersion.V2025_01_05, _ => throw Errors.VersionNotSupported(nameof(version)) }; #endif diff --git a/sdk/storage/Azure.Storage.Common/tests/CommonTestBase.cs b/sdk/storage/Azure.Storage.Common/tests/CommonTestBase.cs index 42d3ed10c84ba..5694c805d3550 100644 --- a/sdk/storage/Azure.Storage.Common/tests/CommonTestBase.cs +++ b/sdk/storage/Azure.Storage.Common/tests/CommonTestBase.cs @@ -34,8 +34,9 @@ namespace Azure.Storage.Test BlobClientOptions.ServiceVersion.V2024_05_04, BlobClientOptions.ServiceVersion.V2024_08_04, BlobClientOptions.ServiceVersion.V2024_11_04, - RecordingServiceVersion = BlobClientOptions.ServiceVersion.V2024_11_04, - LiveServiceVersions = new object[] { BlobClientOptions.ServiceVersion.V2024_08_04, })] + BlobClientOptions.ServiceVersion.V2025_01_05, + RecordingServiceVersion = BlobClientOptions.ServiceVersion.V2025_01_05, + LiveServiceVersions = new object[] { BlobClientOptions.ServiceVersion.V2024_11_04, })] public abstract class CommonTestBase : StorageTestBase { protected readonly BlobClientOptions.ServiceVersion _serviceVersion; diff --git a/sdk/storage/Azure.Storage.DataMovement.Files.Shares/tests/Shared/DisposingShare.cs b/sdk/storage/Azure.Storage.DataMovement.Files.Shares/tests/Shared/DisposingShare.cs index ec0d618c397d5..b703b5cdb55cf 100644 --- a/sdk/storage/Azure.Storage.DataMovement.Files.Shares/tests/Shared/DisposingShare.cs +++ b/sdk/storage/Azure.Storage.DataMovement.Files.Shares/tests/Shared/DisposingShare.cs @@ -18,7 +18,7 @@ public class DisposingShare : IDisposingContainer public static async Task CreateAsync(ShareClient share, IDictionary metadata) { - ShareCreateOptions options = new ShareCreateOptions + BaseShares::Azure.Storage.Files.Shares.Models.ShareCreateOptions options = new() { Metadata = metadata }; diff --git a/sdk/storage/Azure.Storage.Files.DataLake/CHANGELOG.md b/sdk/storage/Azure.Storage.Files.DataLake/CHANGELOG.md index 72b41ecd74dbd..33308a624cadd 100644 --- a/sdk/storage/Azure.Storage.Files.DataLake/CHANGELOG.md +++ b/sdk/storage/Azure.Storage.Files.DataLake/CHANGELOG.md @@ -3,12 +3,10 @@ ## 12.21.0-beta.1 (Unreleased) ### Features Added - -### Breaking Changes - -### Bugs Fixed - -### Other Changes +- Added support for service version 2025-01-05. +- Added GenerateUserDelegationSasUri() for DataLakePathClient, DataLakeFileSystemClient, and DataLakeDirectoryClient +- Deprecated Read()/ReadAsync() in favor of ReadStreaming()/ReadStreamingAsync() and ReadContent()/ReadContentAsync() for DataLake #45418 +- Added GenerateUserDelegationSasUri() to DataLakeFileSystemClient, DataLakePathClient, DataLakeDirectoryClient, and DataLakeFileClient. ## 12.20.0 (2024-09-18) diff --git a/sdk/storage/Azure.Storage.Files.DataLake/README.md b/sdk/storage/Azure.Storage.Files.DataLake/README.md index 249a5c0dced06..e5136a042ff56 100644 --- a/sdk/storage/Azure.Storage.Files.DataLake/README.md +++ b/sdk/storage/Azure.Storage.Files.DataLake/README.md @@ -156,6 +156,18 @@ file.Flush(SampleFileContent.Length); Response fileContents = file.Read(); ``` +### Reading Streaming Data from a DataLake File +```C# Snippet:SampleSnippetDataLakeFileClient_ReadStreaming +Response fileContents = file.ReadStreaming(); +Stream readStream = fileContents.Value.Content; +``` + +### Reading Content Data from a DataLake File +```C# Snippet:SampleSnippetDataLakeFileClient_ReadContent +Response fileContents = file.ReadContent(); +BinaryData readData = fileContents.Value.Content; +``` + ### Listing/Traversing through a DataLake Filesystem ```C# Snippet:SampleSnippetDataLakeFileClient_List foreach (PathItem pathItem in filesystem.GetPaths()) diff --git a/sdk/storage/Azure.Storage.Files.DataLake/api/Azure.Storage.Files.DataLake.net6.0.cs b/sdk/storage/Azure.Storage.Files.DataLake/api/Azure.Storage.Files.DataLake.net6.0.cs index efd9e87cdaeff..c5b8a7798a0cf 100644 --- a/sdk/storage/Azure.Storage.Files.DataLake/api/Azure.Storage.Files.DataLake.net6.0.cs +++ b/sdk/storage/Azure.Storage.Files.DataLake/api/Azure.Storage.Files.DataLake.net6.0.cs @@ -2,7 +2,7 @@ namespace Azure.Storage.Files.DataLake { public partial class DataLakeClientOptions : Azure.Core.ClientOptions { - public DataLakeClientOptions(Azure.Storage.Files.DataLake.DataLakeClientOptions.ServiceVersion version = Azure.Storage.Files.DataLake.DataLakeClientOptions.ServiceVersion.V2024_11_04) { } + public DataLakeClientOptions(Azure.Storage.Files.DataLake.DataLakeClientOptions.ServiceVersion version = Azure.Storage.Files.DataLake.DataLakeClientOptions.ServiceVersion.V2025_01_05) { } public Azure.Storage.Files.DataLake.Models.DataLakeAudience? Audience { get { throw null; } set { } } public Azure.Storage.Files.DataLake.Models.DataLakeCustomerProvidedKey? CustomerProvidedKey { get { throw null; } set { } } public bool EnableTenantDiscovery { get { throw null; } set { } } @@ -35,6 +35,7 @@ public enum ServiceVersion V2024_05_04 = 22, V2024_08_04 = 23, V2024_11_04 = 24, + V2025_01_05 = 25, } } public partial class DataLakeDirectoryClient : Azure.Storage.Files.DataLake.DataLakePathClient @@ -88,6 +89,12 @@ public DataLakeDirectoryClient(System.Uri directoryUri, Azure.Storage.StorageSha public override System.Uri GenerateSasUri(Azure.Storage.Sas.DataLakeSasPermissions permissions, System.DateTimeOffset expiresOn) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public override System.Uri GenerateSasUri(Azure.Storage.Sas.DataLakeSasPermissions permissions, System.DateTimeOffset expiresOn, out string stringToSign) { throw null; } + public override System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.DataLakeSasBuilder builder, Azure.Storage.Files.DataLake.Models.UserDelegationKey userDelegationKey) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public override System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.DataLakeSasBuilder builder, Azure.Storage.Files.DataLake.Models.UserDelegationKey userDelegationKey, out string stringToSign) { throw null; } + public override System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.DataLakeSasPermissions permissions, System.DateTimeOffset expiresOn, Azure.Storage.Files.DataLake.Models.UserDelegationKey userDelegationKey) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public override System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.DataLakeSasPermissions permissions, System.DateTimeOffset expiresOn, Azure.Storage.Files.DataLake.Models.UserDelegationKey userDelegationKey, out string stringToSign) { throw null; } public override Azure.Response GetAccessControl(bool? userPrincipalName = default(bool?), Azure.Storage.Files.DataLake.Models.DataLakeRequestConditions conditions = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public override System.Threading.Tasks.Task> GetAccessControlAsync(bool? userPrincipalName = default(bool?), Azure.Storage.Files.DataLake.Models.DataLakeRequestConditions conditions = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual Azure.Storage.Files.DataLake.DataLakeFileClient GetFileClient(string fileName) { throw null; } @@ -170,16 +177,34 @@ public DataLakeFileClient(System.Uri fileUri, Azure.Storage.StorageSharedKeyCred public virtual System.Threading.Tasks.Task OpenWriteAsync(bool overwrite, Azure.Storage.Files.DataLake.Models.DataLakeFileOpenWriteOptions options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual Azure.Response Query(string querySqlExpression, Azure.Storage.Files.DataLake.Models.DataLakeQueryOptions options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task> QueryAsync(string querySqlExpression, Azure.Storage.Files.DataLake.Models.DataLakeQueryOptions options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public virtual Azure.Response Read() { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public virtual Azure.Response Read(Azure.HttpRange range, Azure.Storage.Files.DataLake.Models.DataLakeRequestConditions conditions, bool rangeGetContentHash, System.Threading.CancellationToken cancellationToken) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public virtual Azure.Response Read(Azure.Storage.Files.DataLake.Models.DataLakeFileReadOptions options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public virtual Azure.Response Read(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public virtual System.Threading.Tasks.Task> ReadAsync() { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public virtual System.Threading.Tasks.Task> ReadAsync(Azure.HttpRange range, Azure.Storage.Files.DataLake.Models.DataLakeRequestConditions conditions, bool rangeGetContentHash, System.Threading.CancellationToken cancellationToken) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public virtual System.Threading.Tasks.Task> ReadAsync(Azure.Storage.Files.DataLake.Models.DataLakeFileReadOptions options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public virtual System.Threading.Tasks.Task> ReadAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual Azure.Response ReadContent() { throw null; } + public virtual Azure.Response ReadContent(Azure.Storage.Files.DataLake.Models.DataLakeFileReadOptions options, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual Azure.Response ReadContent(System.Threading.CancellationToken cancellationToken) { throw null; } + public virtual System.Threading.Tasks.Task> ReadContentAsync() { throw null; } + public virtual System.Threading.Tasks.Task> ReadContentAsync(Azure.Storage.Files.DataLake.Models.DataLakeFileReadOptions options, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual System.Threading.Tasks.Task> ReadContentAsync(System.Threading.CancellationToken cancellationToken) { throw null; } + public virtual Azure.Response ReadStreaming() { throw null; } + public virtual Azure.Response ReadStreaming(Azure.Storage.Files.DataLake.Models.DataLakeFileReadOptions options, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual Azure.Response ReadStreaming(System.Threading.CancellationToken cancellationToken) { throw null; } + public virtual System.Threading.Tasks.Task> ReadStreamingAsync() { throw null; } + public virtual System.Threading.Tasks.Task> ReadStreamingAsync(Azure.Storage.Files.DataLake.Models.DataLakeFileReadOptions options, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual System.Threading.Tasks.Task> ReadStreamingAsync(System.Threading.CancellationToken cancellationToken) { throw null; } public virtual Azure.Response ReadTo(System.IO.Stream destination, Azure.Storage.Files.DataLake.Models.DataLakeFileReadToOptions options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public virtual Azure.Response ReadTo(System.IO.Stream destination, Azure.Storage.Files.DataLake.Models.DataLakeRequestConditions conditions, Azure.Storage.StorageTransferOptions transferOptions, System.Threading.CancellationToken cancellationToken) { throw null; } @@ -283,6 +308,12 @@ public DataLakeFileSystemClient(System.Uri fileSystemUri, Azure.Storage.StorageS public virtual System.Uri GenerateSasUri(Azure.Storage.Sas.DataLakeSasBuilder builder) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public virtual System.Uri GenerateSasUri(Azure.Storage.Sas.DataLakeSasBuilder builder, out string stringToSign) { throw null; } + public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.DataLakeFileSystemSasPermissions permissions, System.DateTimeOffset expiresOn, Azure.Storage.Files.DataLake.Models.UserDelegationKey userDelegationKey) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.DataLakeFileSystemSasPermissions permissions, System.DateTimeOffset expiresOn, Azure.Storage.Files.DataLake.Models.UserDelegationKey userDelegationKey, out string stringToSign) { throw null; } + public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.DataLakeSasBuilder builder, Azure.Storage.Files.DataLake.Models.UserDelegationKey userDelegationKey) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.DataLakeSasBuilder builder, Azure.Storage.Files.DataLake.Models.UserDelegationKey userDelegationKey, out string stringToSign) { throw null; } public virtual Azure.Response GetAccessPolicy(Azure.Storage.Files.DataLake.Models.DataLakeRequestConditions conditions = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task> GetAccessPolicyAsync(Azure.Storage.Files.DataLake.Models.DataLakeRequestConditions conditions = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual Azure.Pageable GetDeletedPaths(string pathPrefix = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } @@ -369,6 +400,12 @@ public DataLakePathClient(System.Uri pathUri, Azure.Storage.StorageSharedKeyCred public virtual System.Uri GenerateSasUri(Azure.Storage.Sas.DataLakeSasPermissions permissions, System.DateTimeOffset expiresOn) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public virtual System.Uri GenerateSasUri(Azure.Storage.Sas.DataLakeSasPermissions permissions, System.DateTimeOffset expiresOn, out string stringToSign) { throw null; } + public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.DataLakeSasBuilder builder, Azure.Storage.Files.DataLake.Models.UserDelegationKey userDelegationKey) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.DataLakeSasBuilder builder, Azure.Storage.Files.DataLake.Models.UserDelegationKey userDelegationKey, out string stringToSign) { throw null; } + public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.DataLakeSasPermissions permissions, System.DateTimeOffset expiresOn, Azure.Storage.Files.DataLake.Models.UserDelegationKey userDelegationKey) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.DataLakeSasPermissions permissions, System.DateTimeOffset expiresOn, Azure.Storage.Files.DataLake.Models.UserDelegationKey userDelegationKey, out string stringToSign) { throw null; } public virtual Azure.Response GetAccessControl(bool? userPrincipalName = default(bool?), Azure.Storage.Files.DataLake.Models.DataLakeRequestConditions conditions = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task> GetAccessControlAsync(bool? userPrincipalName = default(bool?), Azure.Storage.Files.DataLake.Models.DataLakeRequestConditions conditions = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } protected internal virtual Azure.Storage.Files.DataLake.DataLakeDirectoryClient GetParentDirectoryClientCore() { throw null; } @@ -644,6 +681,19 @@ public DataLakeFileReadOptions() { } public Azure.HttpRange Range { get { throw null; } set { } } public Azure.Storage.DownloadTransferValidationOptions TransferValidation { get { throw null; } set { } } } + public partial class DataLakeFileReadResult + { + internal DataLakeFileReadResult() { } + public System.BinaryData Content { get { throw null; } } + public Azure.Storage.Files.DataLake.Models.FileDownloadDetails Details { get { throw null; } } + } + public partial class DataLakeFileReadStreamingResult : System.IDisposable + { + internal DataLakeFileReadStreamingResult() { } + public System.IO.Stream Content { get { throw null; } } + public Azure.Storage.Files.DataLake.Models.FileDownloadDetails Details { get { throw null; } } + public void Dispose() { } + } public partial class DataLakeFileReadToOptions { public DataLakeFileReadToOptions() { } @@ -730,6 +780,8 @@ public DataLakeMetrics() { } } public static partial class DataLakeModelFactory { + public static Azure.Storage.Files.DataLake.Models.DataLakeFileReadResult DataLakeFileReadResult(System.BinaryData content, Azure.Storage.Files.DataLake.Models.FileDownloadDetails details) { throw null; } + public static Azure.Storage.Files.DataLake.Models.DataLakeFileReadStreamingResult DataLakeFileReadStreamingResult(System.IO.Stream content, Azure.Storage.Files.DataLake.Models.FileDownloadDetails details) { throw null; } public static Azure.Storage.Files.DataLake.Models.DataLakeQueryError DataLakeQueryError(string name = null, string description = null, bool isFatal = false, long position = (long)0) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public static Azure.Storage.Files.DataLake.Models.FileDownloadDetails FileDownloadDetails(System.DateTimeOffset lastModified, System.Collections.Generic.IDictionary metadata, string contentRange, Azure.ETag eTag, string contentEncoding, string cacheControl, string contentDisposition, string contentLanguage, System.DateTimeOffset copyCompletionTime, string copyStatusDescription, string copyId, string copyProgress, System.Uri copySource, Azure.Storage.Files.DataLake.Models.CopyStatus copyStatus, Azure.Storage.Files.DataLake.Models.DataLakeLeaseDuration leaseDuration, Azure.Storage.Files.DataLake.Models.DataLakeLeaseState leaseState, Azure.Storage.Files.DataLake.Models.DataLakeLeaseStatus leaseStatus, string acceptRanges, bool isServerEncrypted, string encryptionKeySha256, byte[] contentHash) { throw null; } @@ -738,6 +790,7 @@ public static partial class DataLakeModelFactory [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public static Azure.Storage.Files.DataLake.Models.FileDownloadDetails FileDownloadDetails(System.DateTimeOffset lastModified, System.Collections.Generic.IDictionary metadata, string contentRange, Azure.ETag eTag, string contentEncoding, string cacheControl, string contentDisposition, string contentLanguage, System.DateTimeOffset copyCompletionTime, string copyStatusDescription, string copyId, string copyProgress, System.Uri copySource, Azure.Storage.Files.DataLake.Models.CopyStatus copyStatus, Azure.Storage.Files.DataLake.Models.DataLakeLeaseDuration leaseDuration, Azure.Storage.Files.DataLake.Models.DataLakeLeaseState leaseState, Azure.Storage.Files.DataLake.Models.DataLakeLeaseStatus leaseStatus, string acceptRanges, bool isServerEncrypted, string encryptionKeySha256, byte[] contentHash, System.DateTimeOffset createdOn, string encryptionContext) { throw null; } public static Azure.Storage.Files.DataLake.Models.FileDownloadDetails FileDownloadDetails(System.DateTimeOffset lastModified, System.Collections.Generic.IDictionary metadata, string contentRange, Azure.ETag eTag, string contentEncoding, string cacheControl, string contentDisposition, string contentLanguage, System.DateTimeOffset copyCompletionTime, string copyStatusDescription, string copyId, string copyProgress, System.Uri copySource, Azure.Storage.Files.DataLake.Models.CopyStatus copyStatus, Azure.Storage.Files.DataLake.Models.DataLakeLeaseDuration leaseDuration, Azure.Storage.Files.DataLake.Models.DataLakeLeaseState leaseState, Azure.Storage.Files.DataLake.Models.DataLakeLeaseStatus leaseStatus, string acceptRanges, bool isServerEncrypted, string encryptionKeySha256, byte[] contentHash, System.DateTimeOffset createdOn, string encryptionContext, System.Collections.Generic.IList accessControlList) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public static Azure.Storage.Files.DataLake.Models.FileDownloadInfo FileDownloadInfo(long contentLength, System.IO.Stream content, byte[] contentHash, Azure.Storage.Files.DataLake.Models.FileDownloadDetails properties) { throw null; } public static Azure.Storage.Files.DataLake.Models.FileSystemInfo FileSystemInfo(Azure.ETag etag, System.DateTimeOffset lastModified) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] diff --git a/sdk/storage/Azure.Storage.Files.DataLake/api/Azure.Storage.Files.DataLake.netstandard2.0.cs b/sdk/storage/Azure.Storage.Files.DataLake/api/Azure.Storage.Files.DataLake.netstandard2.0.cs index efd9e87cdaeff..c5b8a7798a0cf 100644 --- a/sdk/storage/Azure.Storage.Files.DataLake/api/Azure.Storage.Files.DataLake.netstandard2.0.cs +++ b/sdk/storage/Azure.Storage.Files.DataLake/api/Azure.Storage.Files.DataLake.netstandard2.0.cs @@ -2,7 +2,7 @@ namespace Azure.Storage.Files.DataLake { public partial class DataLakeClientOptions : Azure.Core.ClientOptions { - public DataLakeClientOptions(Azure.Storage.Files.DataLake.DataLakeClientOptions.ServiceVersion version = Azure.Storage.Files.DataLake.DataLakeClientOptions.ServiceVersion.V2024_11_04) { } + public DataLakeClientOptions(Azure.Storage.Files.DataLake.DataLakeClientOptions.ServiceVersion version = Azure.Storage.Files.DataLake.DataLakeClientOptions.ServiceVersion.V2025_01_05) { } public Azure.Storage.Files.DataLake.Models.DataLakeAudience? Audience { get { throw null; } set { } } public Azure.Storage.Files.DataLake.Models.DataLakeCustomerProvidedKey? CustomerProvidedKey { get { throw null; } set { } } public bool EnableTenantDiscovery { get { throw null; } set { } } @@ -35,6 +35,7 @@ public enum ServiceVersion V2024_05_04 = 22, V2024_08_04 = 23, V2024_11_04 = 24, + V2025_01_05 = 25, } } public partial class DataLakeDirectoryClient : Azure.Storage.Files.DataLake.DataLakePathClient @@ -88,6 +89,12 @@ public DataLakeDirectoryClient(System.Uri directoryUri, Azure.Storage.StorageSha public override System.Uri GenerateSasUri(Azure.Storage.Sas.DataLakeSasPermissions permissions, System.DateTimeOffset expiresOn) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public override System.Uri GenerateSasUri(Azure.Storage.Sas.DataLakeSasPermissions permissions, System.DateTimeOffset expiresOn, out string stringToSign) { throw null; } + public override System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.DataLakeSasBuilder builder, Azure.Storage.Files.DataLake.Models.UserDelegationKey userDelegationKey) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public override System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.DataLakeSasBuilder builder, Azure.Storage.Files.DataLake.Models.UserDelegationKey userDelegationKey, out string stringToSign) { throw null; } + public override System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.DataLakeSasPermissions permissions, System.DateTimeOffset expiresOn, Azure.Storage.Files.DataLake.Models.UserDelegationKey userDelegationKey) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public override System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.DataLakeSasPermissions permissions, System.DateTimeOffset expiresOn, Azure.Storage.Files.DataLake.Models.UserDelegationKey userDelegationKey, out string stringToSign) { throw null; } public override Azure.Response GetAccessControl(bool? userPrincipalName = default(bool?), Azure.Storage.Files.DataLake.Models.DataLakeRequestConditions conditions = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public override System.Threading.Tasks.Task> GetAccessControlAsync(bool? userPrincipalName = default(bool?), Azure.Storage.Files.DataLake.Models.DataLakeRequestConditions conditions = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual Azure.Storage.Files.DataLake.DataLakeFileClient GetFileClient(string fileName) { throw null; } @@ -170,16 +177,34 @@ public DataLakeFileClient(System.Uri fileUri, Azure.Storage.StorageSharedKeyCred public virtual System.Threading.Tasks.Task OpenWriteAsync(bool overwrite, Azure.Storage.Files.DataLake.Models.DataLakeFileOpenWriteOptions options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual Azure.Response Query(string querySqlExpression, Azure.Storage.Files.DataLake.Models.DataLakeQueryOptions options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task> QueryAsync(string querySqlExpression, Azure.Storage.Files.DataLake.Models.DataLakeQueryOptions options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public virtual Azure.Response Read() { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public virtual Azure.Response Read(Azure.HttpRange range, Azure.Storage.Files.DataLake.Models.DataLakeRequestConditions conditions, bool rangeGetContentHash, System.Threading.CancellationToken cancellationToken) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public virtual Azure.Response Read(Azure.Storage.Files.DataLake.Models.DataLakeFileReadOptions options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public virtual Azure.Response Read(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public virtual System.Threading.Tasks.Task> ReadAsync() { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public virtual System.Threading.Tasks.Task> ReadAsync(Azure.HttpRange range, Azure.Storage.Files.DataLake.Models.DataLakeRequestConditions conditions, bool rangeGetContentHash, System.Threading.CancellationToken cancellationToken) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public virtual System.Threading.Tasks.Task> ReadAsync(Azure.Storage.Files.DataLake.Models.DataLakeFileReadOptions options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public virtual System.Threading.Tasks.Task> ReadAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual Azure.Response ReadContent() { throw null; } + public virtual Azure.Response ReadContent(Azure.Storage.Files.DataLake.Models.DataLakeFileReadOptions options, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual Azure.Response ReadContent(System.Threading.CancellationToken cancellationToken) { throw null; } + public virtual System.Threading.Tasks.Task> ReadContentAsync() { throw null; } + public virtual System.Threading.Tasks.Task> ReadContentAsync(Azure.Storage.Files.DataLake.Models.DataLakeFileReadOptions options, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual System.Threading.Tasks.Task> ReadContentAsync(System.Threading.CancellationToken cancellationToken) { throw null; } + public virtual Azure.Response ReadStreaming() { throw null; } + public virtual Azure.Response ReadStreaming(Azure.Storage.Files.DataLake.Models.DataLakeFileReadOptions options, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual Azure.Response ReadStreaming(System.Threading.CancellationToken cancellationToken) { throw null; } + public virtual System.Threading.Tasks.Task> ReadStreamingAsync() { throw null; } + public virtual System.Threading.Tasks.Task> ReadStreamingAsync(Azure.Storage.Files.DataLake.Models.DataLakeFileReadOptions options, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual System.Threading.Tasks.Task> ReadStreamingAsync(System.Threading.CancellationToken cancellationToken) { throw null; } public virtual Azure.Response ReadTo(System.IO.Stream destination, Azure.Storage.Files.DataLake.Models.DataLakeFileReadToOptions options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public virtual Azure.Response ReadTo(System.IO.Stream destination, Azure.Storage.Files.DataLake.Models.DataLakeRequestConditions conditions, Azure.Storage.StorageTransferOptions transferOptions, System.Threading.CancellationToken cancellationToken) { throw null; } @@ -283,6 +308,12 @@ public DataLakeFileSystemClient(System.Uri fileSystemUri, Azure.Storage.StorageS public virtual System.Uri GenerateSasUri(Azure.Storage.Sas.DataLakeSasBuilder builder) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public virtual System.Uri GenerateSasUri(Azure.Storage.Sas.DataLakeSasBuilder builder, out string stringToSign) { throw null; } + public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.DataLakeFileSystemSasPermissions permissions, System.DateTimeOffset expiresOn, Azure.Storage.Files.DataLake.Models.UserDelegationKey userDelegationKey) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.DataLakeFileSystemSasPermissions permissions, System.DateTimeOffset expiresOn, Azure.Storage.Files.DataLake.Models.UserDelegationKey userDelegationKey, out string stringToSign) { throw null; } + public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.DataLakeSasBuilder builder, Azure.Storage.Files.DataLake.Models.UserDelegationKey userDelegationKey) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.DataLakeSasBuilder builder, Azure.Storage.Files.DataLake.Models.UserDelegationKey userDelegationKey, out string stringToSign) { throw null; } public virtual Azure.Response GetAccessPolicy(Azure.Storage.Files.DataLake.Models.DataLakeRequestConditions conditions = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task> GetAccessPolicyAsync(Azure.Storage.Files.DataLake.Models.DataLakeRequestConditions conditions = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual Azure.Pageable GetDeletedPaths(string pathPrefix = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } @@ -369,6 +400,12 @@ public DataLakePathClient(System.Uri pathUri, Azure.Storage.StorageSharedKeyCred public virtual System.Uri GenerateSasUri(Azure.Storage.Sas.DataLakeSasPermissions permissions, System.DateTimeOffset expiresOn) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public virtual System.Uri GenerateSasUri(Azure.Storage.Sas.DataLakeSasPermissions permissions, System.DateTimeOffset expiresOn, out string stringToSign) { throw null; } + public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.DataLakeSasBuilder builder, Azure.Storage.Files.DataLake.Models.UserDelegationKey userDelegationKey) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.DataLakeSasBuilder builder, Azure.Storage.Files.DataLake.Models.UserDelegationKey userDelegationKey, out string stringToSign) { throw null; } + public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.DataLakeSasPermissions permissions, System.DateTimeOffset expiresOn, Azure.Storage.Files.DataLake.Models.UserDelegationKey userDelegationKey) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.DataLakeSasPermissions permissions, System.DateTimeOffset expiresOn, Azure.Storage.Files.DataLake.Models.UserDelegationKey userDelegationKey, out string stringToSign) { throw null; } public virtual Azure.Response GetAccessControl(bool? userPrincipalName = default(bool?), Azure.Storage.Files.DataLake.Models.DataLakeRequestConditions conditions = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task> GetAccessControlAsync(bool? userPrincipalName = default(bool?), Azure.Storage.Files.DataLake.Models.DataLakeRequestConditions conditions = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } protected internal virtual Azure.Storage.Files.DataLake.DataLakeDirectoryClient GetParentDirectoryClientCore() { throw null; } @@ -644,6 +681,19 @@ public DataLakeFileReadOptions() { } public Azure.HttpRange Range { get { throw null; } set { } } public Azure.Storage.DownloadTransferValidationOptions TransferValidation { get { throw null; } set { } } } + public partial class DataLakeFileReadResult + { + internal DataLakeFileReadResult() { } + public System.BinaryData Content { get { throw null; } } + public Azure.Storage.Files.DataLake.Models.FileDownloadDetails Details { get { throw null; } } + } + public partial class DataLakeFileReadStreamingResult : System.IDisposable + { + internal DataLakeFileReadStreamingResult() { } + public System.IO.Stream Content { get { throw null; } } + public Azure.Storage.Files.DataLake.Models.FileDownloadDetails Details { get { throw null; } } + public void Dispose() { } + } public partial class DataLakeFileReadToOptions { public DataLakeFileReadToOptions() { } @@ -730,6 +780,8 @@ public DataLakeMetrics() { } } public static partial class DataLakeModelFactory { + public static Azure.Storage.Files.DataLake.Models.DataLakeFileReadResult DataLakeFileReadResult(System.BinaryData content, Azure.Storage.Files.DataLake.Models.FileDownloadDetails details) { throw null; } + public static Azure.Storage.Files.DataLake.Models.DataLakeFileReadStreamingResult DataLakeFileReadStreamingResult(System.IO.Stream content, Azure.Storage.Files.DataLake.Models.FileDownloadDetails details) { throw null; } public static Azure.Storage.Files.DataLake.Models.DataLakeQueryError DataLakeQueryError(string name = null, string description = null, bool isFatal = false, long position = (long)0) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public static Azure.Storage.Files.DataLake.Models.FileDownloadDetails FileDownloadDetails(System.DateTimeOffset lastModified, System.Collections.Generic.IDictionary metadata, string contentRange, Azure.ETag eTag, string contentEncoding, string cacheControl, string contentDisposition, string contentLanguage, System.DateTimeOffset copyCompletionTime, string copyStatusDescription, string copyId, string copyProgress, System.Uri copySource, Azure.Storage.Files.DataLake.Models.CopyStatus copyStatus, Azure.Storage.Files.DataLake.Models.DataLakeLeaseDuration leaseDuration, Azure.Storage.Files.DataLake.Models.DataLakeLeaseState leaseState, Azure.Storage.Files.DataLake.Models.DataLakeLeaseStatus leaseStatus, string acceptRanges, bool isServerEncrypted, string encryptionKeySha256, byte[] contentHash) { throw null; } @@ -738,6 +790,7 @@ public static partial class DataLakeModelFactory [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public static Azure.Storage.Files.DataLake.Models.FileDownloadDetails FileDownloadDetails(System.DateTimeOffset lastModified, System.Collections.Generic.IDictionary metadata, string contentRange, Azure.ETag eTag, string contentEncoding, string cacheControl, string contentDisposition, string contentLanguage, System.DateTimeOffset copyCompletionTime, string copyStatusDescription, string copyId, string copyProgress, System.Uri copySource, Azure.Storage.Files.DataLake.Models.CopyStatus copyStatus, Azure.Storage.Files.DataLake.Models.DataLakeLeaseDuration leaseDuration, Azure.Storage.Files.DataLake.Models.DataLakeLeaseState leaseState, Azure.Storage.Files.DataLake.Models.DataLakeLeaseStatus leaseStatus, string acceptRanges, bool isServerEncrypted, string encryptionKeySha256, byte[] contentHash, System.DateTimeOffset createdOn, string encryptionContext) { throw null; } public static Azure.Storage.Files.DataLake.Models.FileDownloadDetails FileDownloadDetails(System.DateTimeOffset lastModified, System.Collections.Generic.IDictionary metadata, string contentRange, Azure.ETag eTag, string contentEncoding, string cacheControl, string contentDisposition, string contentLanguage, System.DateTimeOffset copyCompletionTime, string copyStatusDescription, string copyId, string copyProgress, System.Uri copySource, Azure.Storage.Files.DataLake.Models.CopyStatus copyStatus, Azure.Storage.Files.DataLake.Models.DataLakeLeaseDuration leaseDuration, Azure.Storage.Files.DataLake.Models.DataLakeLeaseState leaseState, Azure.Storage.Files.DataLake.Models.DataLakeLeaseStatus leaseStatus, string acceptRanges, bool isServerEncrypted, string encryptionKeySha256, byte[] contentHash, System.DateTimeOffset createdOn, string encryptionContext, System.Collections.Generic.IList accessControlList) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public static Azure.Storage.Files.DataLake.Models.FileDownloadInfo FileDownloadInfo(long contentLength, System.IO.Stream content, byte[] contentHash, Azure.Storage.Files.DataLake.Models.FileDownloadDetails properties) { throw null; } public static Azure.Storage.Files.DataLake.Models.FileSystemInfo FileSystemInfo(Azure.ETag etag, System.DateTimeOffset lastModified) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] diff --git a/sdk/storage/Azure.Storage.Files.DataLake/assets.json b/sdk/storage/Azure.Storage.Files.DataLake/assets.json index 4bb0d300192cf..4a64b8398f656 100644 --- a/sdk/storage/Azure.Storage.Files.DataLake/assets.json +++ b/sdk/storage/Azure.Storage.Files.DataLake/assets.json @@ -2,5 +2,5 @@ "AssetsRepo": "Azure/azure-sdk-assets", "AssetsRepoPrefixPath": "net", "TagPrefix": "net/storage/Azure.Storage.Files.DataLake", - "Tag": "net/storage/Azure.Storage.Files.DataLake_c97d1635f5" + "Tag": "net/storage/Azure.Storage.Files.DataLake_d74597f1e3" } diff --git a/sdk/storage/Azure.Storage.Files.DataLake/samples/Sample01a_HelloWorld.cs b/sdk/storage/Azure.Storage.Files.DataLake/samples/Sample01a_HelloWorld.cs index 3f8cfdd32c7c5..f8369e587453a 100644 --- a/sdk/storage/Azure.Storage.Files.DataLake/samples/Sample01a_HelloWorld.cs +++ b/sdk/storage/Azure.Storage.Files.DataLake/samples/Sample01a_HelloWorld.cs @@ -325,6 +325,115 @@ public void Read() } } + /// + /// Download a DataLake File's streaming data to a file. + /// + [Test] + public void ReadStreaming() + { + // Create a temporary Lorem Ipsum file on disk that we can upload + string originalPath = CreateTempFile(SampleFileContent); + + // Get a temporary path on disk where we can download the file + string downloadPath = CreateTempPath(); + + // Make StorageSharedKeyCredential to pass to the serviceClient + string storageAccountName = StorageAccountName; + string storageAccountKey = StorageAccountKey; + Uri serviceUri = StorageAccountBlobUri; + StorageSharedKeyCredential sharedKeyCredential = new StorageSharedKeyCredential(storageAccountName, storageAccountKey); + + // Create DataLakeServiceClient using StorageSharedKeyCredentials + DataLakeServiceClient serviceClient = new DataLakeServiceClient(serviceUri, sharedKeyCredential); + + // Get a reference to a filesystem named "sample-filesystem-read" and then create it + DataLakeFileSystemClient filesystem = serviceClient.GetFileSystemClient("sample-filesystem-read"); + filesystem.Create(); + try + { + // Get a reference to a file named "sample-file" in a filesystem + DataLakeFileClient file = filesystem.GetFileClient("sample-file"); + + // First upload something the DataLake file so we have something to download + file.Upload(File.OpenRead(originalPath)); + + // Download the DataLake file's contents and save it to a file + // The ReadStreamingAsync() API downloads a file in a single requests. + // For large files, it may be faster to call ReadTo() + #region Snippet:SampleSnippetDataLakeFileClient_ReadStreaming + Response fileContents = file.ReadStreaming(); + Stream readStream = fileContents.Value.Content; + #endregion Snippet:SampleSnippetDataLakeFileClient_ReadStreaming + using (FileStream stream = File.OpenWrite(downloadPath)) + { + readStream.CopyTo(stream); + } + + // Verify the contents + Assert.AreEqual(SampleFileContent, File.ReadAllText(downloadPath)); + } + finally + { + // Clean up after the test when we're finished + filesystem.Delete(); + } + } + + /// + /// Download a DataLake File's content data to a file. + /// + [Test] + public void ReadContent() + { + // Create a temporary Lorem Ipsum file on disk that we can upload + string originalPath = CreateTempFile(SampleFileContent); + + // Get a temporary path on disk where we can download the file + string downloadPath = CreateTempPath(); + + // Make StorageSharedKeyCredential to pass to the serviceClient + string storageAccountName = StorageAccountName; + string storageAccountKey = StorageAccountKey; + Uri serviceUri = StorageAccountBlobUri; + StorageSharedKeyCredential sharedKeyCredential = new StorageSharedKeyCredential(storageAccountName, storageAccountKey); + + // Create DataLakeServiceClient using StorageSharedKeyCredentials + DataLakeServiceClient serviceClient = new DataLakeServiceClient(serviceUri, sharedKeyCredential); + + // Get a reference to a filesystem named "sample-filesystem-read" and then create it + DataLakeFileSystemClient filesystem = serviceClient.GetFileSystemClient("sample-filesystem-read"); + filesystem.Create(); + try + { + // Get a reference to a file named "sample-file" in a filesystem + DataLakeFileClient file = filesystem.GetFileClient("sample-file"); + + // First upload something the DataLake file so we have something to download + file.Upload(File.OpenRead(originalPath)); + + // Download the DataLake file's contents and save it to a file + // The ReadContentAsync() API downloads a file in a single requests. + // For large files, it may be faster to call ReadTo() + #region Snippet:SampleSnippetDataLakeFileClient_ReadContent + Response fileContents = file.ReadContent(); + BinaryData readData = fileContents.Value.Content; + #endregion Snippet:SampleSnippetDataLakeFileClient_ReadContent + byte[] data = readData.ToArray(); + using (FileStream stream = File.OpenWrite(downloadPath)) + { + stream.Write(data, 0, data.Length); + } + + // Verify the contents + Assert.AreEqual(SampleFileContent, File.ReadAllText(downloadPath)); + } + finally + { + // Clean up after the test when we're finished + filesystem.Delete(); + } + } + /// /// Download a DataLake File to a file. /// diff --git a/sdk/storage/Azure.Storage.Files.DataLake/samples/Sample01b_HelloWorldAsync.cs b/sdk/storage/Azure.Storage.Files.DataLake/samples/Sample01b_HelloWorldAsync.cs index c00d412854193..227cdfaf27a7b 100644 --- a/sdk/storage/Azure.Storage.Files.DataLake/samples/Sample01b_HelloWorldAsync.cs +++ b/sdk/storage/Azure.Storage.Files.DataLake/samples/Sample01b_HelloWorldAsync.cs @@ -327,6 +327,111 @@ public async Task ReadAsync() } } + /// + /// Download a DataLake File's streaming data to a file. + /// + [Test] + public async Task ReadStreamingAsync() + { + // Create a temporary Lorem Ipsum file on disk that we can upload + string originalPath = CreateTempFile(SampleFileContent); + + // Get a temporary path on disk where we can download the file + string downloadPath = CreateTempPath(); + + // Make StorageSharedKeyCredential to pass to the serviceClient + string storageAccountName = StorageAccountName; + string storageAccountKey = StorageAccountKey; + Uri serviceUri = StorageAccountBlobUri; + StorageSharedKeyCredential sharedKeyCredential = new StorageSharedKeyCredential(storageAccountName, storageAccountKey); + + // Create DataLakeServiceClient using StorageSharedKeyCredentials + DataLakeServiceClient serviceClient = new DataLakeServiceClient(serviceUri, sharedKeyCredential); + + // Get a reference to a filesystem named "sample-filesystem-readasync" and then create it + DataLakeFileSystemClient filesystem = serviceClient.GetFileSystemClient(Randomize("sample-filesystem-read")); + await filesystem.CreateAsync(); + try + { + // Get a reference to a file named "sample-file" in a filesystem + DataLakeFileClient file = filesystem.GetFileClient(Randomize("sample-file")); + + // First upload something the DataLake file so we have something to download + await file.UploadAsync(File.OpenRead(originalPath)); + + // Download the DataLake file's contents and save it to a file + // The ReadStreamingAsync() API downloads a file in a single requests. + // For large files, it may be faster to call ReadToAsync() + Response fileContents = await file.ReadStreamingAsync(); + Stream readStream = fileContents.Value.Content; + using (FileStream stream = File.OpenWrite(downloadPath)) + { + readStream.CopyTo(stream); + } + + // Verify the contents + Assert.AreEqual(SampleFileContent, File.ReadAllText(downloadPath)); + } + finally + { + // Clean up after the test when we're finished + await filesystem.DeleteAsync(); + } + } + + /// + /// Download a DataLake File's content data to a file. + /// + [Test] + public async Task ReadContentAsync() + { + // Create a temporary Lorem Ipsum file on disk that we can upload + string originalPath = CreateTempFile(SampleFileContent); + + // Get a temporary path on disk where we can download the file + string downloadPath = CreateTempPath(); + + // Make StorageSharedKeyCredential to pass to the serviceClient + string storageAccountName = StorageAccountName; + string storageAccountKey = StorageAccountKey; + Uri serviceUri = StorageAccountBlobUri; + StorageSharedKeyCredential sharedKeyCredential = new StorageSharedKeyCredential(storageAccountName, storageAccountKey); + + // Create DataLakeServiceClient using StorageSharedKeyCredentials + DataLakeServiceClient serviceClient = new DataLakeServiceClient(serviceUri, sharedKeyCredential); + + // Get a reference to a filesystem named "sample-filesystem-readasync" and then create it + DataLakeFileSystemClient filesystem = serviceClient.GetFileSystemClient(Randomize("sample-filesystem-read")); + await filesystem.CreateAsync(); + try + { + // Get a reference to a file named "sample-file" in a filesystem + DataLakeFileClient file = filesystem.GetFileClient(Randomize("sample-file")); + + // First upload something the DataLake file so we have something to download + await file.UploadAsync(File.OpenRead(originalPath)); + + // Download the DataLake file's contents and save it to a file + // The ReadContentAsync() API downloads a file in a single requests. + // For large files, it may be faster to call ReadToAsync() + Response fileContents = await file.ReadContentAsync(); + BinaryData readData = fileContents.Value.Content; + byte[] data = readData.ToArray(); + using (FileStream stream = File.OpenWrite(downloadPath)) + { + stream.Write(data, 0, data.Length); + } + + // Verify the contents + Assert.AreEqual(SampleFileContent, File.ReadAllText(downloadPath)); + } + finally + { + // Clean up after the test when we're finished + await filesystem.DeleteAsync(); + } + } + /// /// Download a DataLake File directly to file. /// diff --git a/sdk/storage/Azure.Storage.Files.DataLake/src/DataLakeClientOptions.cs b/sdk/storage/Azure.Storage.Files.DataLake/src/DataLakeClientOptions.cs index 6fa6ec5166bbd..5f8fd0849ba0f 100644 --- a/sdk/storage/Azure.Storage.Files.DataLake/src/DataLakeClientOptions.cs +++ b/sdk/storage/Azure.Storage.Files.DataLake/src/DataLakeClientOptions.cs @@ -151,7 +151,12 @@ public enum ServiceVersion /// /// The 2024-11-04 service version. /// - V2024_11_04 = 24 + V2024_11_04 = 24, + + /// + /// The 2025-01-05 service version. + /// + V2025_01_05 = 25 #pragma warning restore CA1707 // Identifiers should not contain underscores } diff --git a/sdk/storage/Azure.Storage.Files.DataLake/src/DataLakeDirectoryClient.cs b/sdk/storage/Azure.Storage.Files.DataLake/src/DataLakeDirectoryClient.cs index 41b75adc88d1f..7b2ba5c7cee3b 100644 --- a/sdk/storage/Azure.Storage.Files.DataLake/src/DataLakeDirectoryClient.cs +++ b/sdk/storage/Azure.Storage.Files.DataLake/src/DataLakeDirectoryClient.cs @@ -3059,11 +3059,170 @@ public override Uri GenerateSasUri(DataLakeSasBuilder builder, out string string // Deep copy of builder so we don't modify the user's original DataLakeSasBuilder. builder = DataLakeSasBuilder.DeepCopy(builder); + SetBuilderAndValidate(builder); + DataLakeUriBuilder sasUri = new DataLakeUriBuilder(Uri) + { + Sas = builder.ToSasQueryParameters(ClientConfiguration.SharedKeyCredential, out stringToSign) + }; + return sasUri.ToUri(); + } + #endregion + + #region GenerateUserDelegationSas + /// + /// The + /// returns a that generates a DataLake Directory Service Shared Access Signature (SAS) + /// Uri based on the Client properties and parameter passed. The SAS is signed by the user delegation key passed in. + /// + /// For more information, see + /// + /// Creating an user delegation SAS. + /// + /// + /// Required. Specifies the list of permissions to be associated with the SAS. + /// See . + /// + /// + /// Required. Specifies the time at which the SAS becomes invalid. This field + /// must be omitted if it has been specified in an associated stored access policy. + /// + /// + /// Required. A returned from + /// . + /// + /// + /// A containing the SAS Uri. + /// + /// + /// A will be thrown if a failure occurs. + /// + [CallerShouldAudit("https://aka.ms/azsdk/callershouldaudit/storage-files-datalake")] + public override Uri GenerateUserDelegationSasUri(DataLakeSasPermissions permissions, DateTimeOffset expiresOn, UserDelegationKey userDelegationKey) + => GenerateUserDelegationSasUri(permissions, expiresOn, userDelegationKey, out _); + + /// + /// The + /// returns a that generates a DataLake Directory Service Shared Access Signature (SAS) + /// Uri based on the Client properties and parameter passed. The SAS is signed by the user delegation key passed in. + /// + /// For more information, see + /// + /// Creating an user delegation SAS. + /// + /// + /// Required. Specifies the list of permissions to be associated with the SAS. + /// See . + /// + /// + /// Required. Specifies the time at which the SAS becomes invalid. This field + /// must be omitted if it has been specified in an associated stored access policy. + /// + /// + /// Required. A returned from + /// . + /// + /// + /// For debugging purposes only. This string will be overwritten with the string to sign that was used to generate the SAS Uri. + /// + /// + /// A containing the SAS Uri. + /// + /// + /// A will be thrown if a failure occurs. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + [CallerShouldAudit("https://aka.ms/azsdk/callershouldaudit/storage-files-datalake")] + public override Uri GenerateUserDelegationSasUri(DataLakeSasPermissions permissions, DateTimeOffset expiresOn, UserDelegationKey userDelegationKey, out string stringToSign) => + GenerateUserDelegationSasUri(new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = FileSystemName, + Path = Path, + IsDirectory = true + }, userDelegationKey, out stringToSign); + + /// + /// The + /// returns a that generates a DataLake Directory Service Shared Access Signature (SAS) + /// Uri based on the Client properties and builder passed. The SAS is signed by the user delegation key passed in. + /// + /// For more information, see + /// + /// Creating an user delegation SAS. + /// + /// + /// Required. Used to generate a Shared Access Signature (SAS). + /// + /// + /// Required. A returned from + /// . + /// + /// + /// A containing the SAS Uri. + /// + /// + /// A will be thrown if a failure occurs. + /// + [CallerShouldAudit("https://aka.ms/azsdk/callershouldaudit/storage-files-datalake")] + public override Uri GenerateUserDelegationSasUri(DataLakeSasBuilder builder, UserDelegationKey userDelegationKey) + => GenerateUserDelegationSasUri(builder, userDelegationKey, out _); + + /// + /// The + /// returns a that generates a DataLake Directory Service Shared Access Signature (SAS) + /// Uri based on the Client properties and builder passed. The SAS is signed by the user delegation key passed in. + /// + /// For more information, see + /// + /// Creating an user delegation SAS. + /// + /// + /// Required. Used to generate a Shared Access Signature (SAS). + /// + /// + /// Required. A returned from + /// . + /// + /// + /// For debugging purposes only. This string will be overwritten with the string to sign that was used to generate the SAS Uri. + /// + /// + /// A containing the SAS Uri. + /// + /// + /// A will be thrown if a failure occurs. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + [CallerShouldAudit("https://aka.ms/azsdk/callershouldaudit/storage-files-datalake")] + public override Uri GenerateUserDelegationSasUri(DataLakeSasBuilder builder, UserDelegationKey userDelegationKey, out string stringToSign) + { + builder = builder ?? throw Errors.ArgumentNull(nameof(builder)); + userDelegationKey = userDelegationKey ?? throw Errors.ArgumentNull(nameof(userDelegationKey)); + + // Deep copy of builder so we don't modify the user's original DataLakeSasBuilder. + builder = DataLakeSasBuilder.DeepCopy(builder); + + SetBuilderAndValidate(builder); + if (string.IsNullOrEmpty(AccountName)) + { + throw Errors.SasClientMissingData(nameof(AccountName)); + } + + DataLakeUriBuilder sasUri = new DataLakeUriBuilder(Uri) + { + Sas = builder.ToSasQueryParameters(userDelegationKey, AccountName, out stringToSign) + }; + return sasUri.ToUri(); + } + #endregion + + private void SetBuilderAndValidate(DataLakeSasBuilder builder) + { // Assign builder's IsDirectory, FileSystemName, and Path, if they are null. builder.IsDirectory ??= GetType() == typeof(DataLakeDirectoryClient); builder.FileSystemName ??= FileSystemName; builder.Path ??= Path; + // Validate that builder is properly set if (!builder.IsDirectory.GetValueOrDefault(false)) { throw Errors.SasIncorrectResourceType( @@ -3086,12 +3245,6 @@ public override Uri GenerateSasUri(DataLakeSasBuilder builder, out string string nameof(DataLakeSasBuilder), nameof(Path)); } - DataLakeUriBuilder sasUri = new DataLakeUriBuilder(Uri) - { - Sas = builder.ToSasQueryParameters(ClientConfiguration.SharedKeyCredential, out stringToSign) - }; - return sasUri.ToUri(); } - #endregion } } diff --git a/sdk/storage/Azure.Storage.Files.DataLake/src/DataLakeExtensions.cs b/sdk/storage/Azure.Storage.Files.DataLake/src/DataLakeExtensions.cs index 9d50525a1120c..77f5257c56923 100644 --- a/sdk/storage/Azure.Storage.Files.DataLake/src/DataLakeExtensions.cs +++ b/sdk/storage/Azure.Storage.Files.DataLake/src/DataLakeExtensions.cs @@ -99,6 +99,29 @@ internal static FileDownloadInfo ToFileDownloadInfo(this Response blobDownloadStreamingResultResponse) + { + blobDownloadStreamingResultResponse.GetRawResponse().Headers.TryGetValue(Constants.DataLake.EncryptionContextHeaderName, out string encryptionContext); + blobDownloadStreamingResultResponse.GetRawResponse().Headers.TryGetValue(Constants.DataLake.AclHeaderName, out string accessControlList); + DataLakeFileReadStreamingResult dataLakeFileReadStreamingResult = new DataLakeFileReadStreamingResult() + { + Content = blobDownloadStreamingResultResponse.Value.Content, + Details = blobDownloadStreamingResultResponse.Value.Details.ToFileDownloadDetails(encryptionContext, accessControlList) + }; + return dataLakeFileReadStreamingResult; + } + + internal static DataLakeFileReadResult ToDataLakeFileReadResult(this Response blobDownloadResult) + { + blobDownloadResult.GetRawResponse().Headers.TryGetValue(Constants.DataLake.EncryptionContextHeaderName, out string encryptionContext); + blobDownloadResult.GetRawResponse().Headers.TryGetValue(Constants.DataLake.AclHeaderName, out string accessControlList); + DataLakeFileReadResult dataLakeFileReadResult = new DataLakeFileReadResult() + { + Content = blobDownloadResult.Value.Content, + Details = blobDownloadResult.Value.Details.ToFileDownloadDetails(encryptionContext, accessControlList) + }; + return dataLakeFileReadResult; + } internal static PathProperties ToPathProperties(this Response blobPropertiesResponse) { diff --git a/sdk/storage/Azure.Storage.Files.DataLake/src/DataLakeFileClient.cs b/sdk/storage/Azure.Storage.Files.DataLake/src/DataLakeFileClient.cs index b2221f513461e..e755faff2f5a7 100644 --- a/sdk/storage/Azure.Storage.Files.DataLake/src/DataLakeFileClient.cs +++ b/sdk/storage/Azure.Storage.Files.DataLake/src/DataLakeFileClient.cs @@ -2827,6 +2827,8 @@ internal virtual async Task> FlushInternal( #endregion #region Read Data + + #region Deprecated /// /// The operation downloads a file from /// the service, including its metadata and properties. @@ -2844,6 +2846,7 @@ internal virtual async Task> FlushInternal( /// A will be thrown if /// a failure occurs. /// + [EditorBrowsable(EditorBrowsableState.Never)] public virtual Response Read() { DiagnosticScope scope = ClientConfiguration.ClientDiagnostics.CreateScope($"{nameof(DataLakeFileClient)}.{nameof(Read)}"); @@ -2886,6 +2889,7 @@ public virtual Response Read() /// A will be thrown if /// a failure occurs. /// + [EditorBrowsable(EditorBrowsableState.Never)] public virtual async Task> ReadAsync() { DiagnosticScope scope = ClientConfiguration.ClientDiagnostics.CreateScope($"{nameof(DataLakeFileClient)}.{nameof(Read)}"); @@ -2933,6 +2937,7 @@ public virtual async Task> ReadAsync() /// A will be thrown if /// a failure occurs. /// + [EditorBrowsable(EditorBrowsableState.Never)] public virtual Response Read( CancellationToken cancellationToken = default) { @@ -2980,6 +2985,7 @@ public virtual Response Read( /// A will be thrown if /// a failure occurs. /// + [EditorBrowsable(EditorBrowsableState.Never)] public virtual async Task> ReadAsync( CancellationToken cancellationToken = default) { @@ -3017,12 +3023,12 @@ public virtual async Task> ReadAsync( /// Get Blob. /// /// - /// If provided, only donwload the bytes of the file in the specified + /// If provided, only download the bytes of the file in the specified /// range. If not provided, download the entire file. /// /// /// Optional to add conditions on - /// donwloading this file. + /// downloading this file. /// /// /// When set to true and specified together with the , @@ -3091,12 +3097,12 @@ public virtual Response Read( /// Get Blob. /// /// - /// If provided, only donwload the bytes of the file in the specified + /// If provided, only download the bytes of the file in the specified /// range. If not provided, download the entire file. /// /// /// Optional to add conditions on - /// donwloading this file. + /// downloading this file. /// /// /// When set to true and specified together with the , @@ -3157,7 +3163,7 @@ public virtual async Task> ReadAsync( } /// - /// The + /// The /// operation downloads a file from the service, including its metadata /// and properties. /// @@ -3181,6 +3187,7 @@ public virtual async Task> ReadAsync( /// A will be thrown if /// a failure occurs. /// + [EditorBrowsable(EditorBrowsableState.Never)] public virtual Response Read( DataLakeFileReadOptions options = default, CancellationToken cancellationToken = default) @@ -3211,7 +3218,7 @@ public virtual Response Read( } /// - /// The + /// The /// operation downloads a file from the service, including its metadata /// and properties. /// @@ -3235,6 +3242,7 @@ public virtual Response Read( /// A will be thrown if /// a failure occurs. /// + [EditorBrowsable(EditorBrowsableState.Never)] public virtual async Task> ReadAsync( DataLakeFileReadOptions options = default, CancellationToken cancellationToken = default) @@ -3264,6 +3272,597 @@ public virtual async Task> ReadAsync( scope.Dispose(); } } + #endregion Deprecated + + /// + /// The + /// operation downloads a file from the service, including its metadata + /// and properties. + /// + /// For more information, see + /// + /// Get Blob. + /// + /// + /// A describing the + /// downloaded file. contains + /// the file's data. + /// + /// + /// A will be thrown if + /// a failure occurs. + /// + public virtual Response ReadStreaming() + { + DiagnosticScope scope = ClientConfiguration.ClientDiagnostics.CreateScope($"{nameof(DataLakeFileClient)}.{nameof(ReadStreaming)}"); + + try + { + scope.Start(); + + Response response = _blockBlobClient.DownloadStreaming(); + + return Response.FromValue( + response.ToDataLakeFileReadStreamingResult(), + response.GetRawResponse()); + } + catch (Exception ex) + { + scope.Failed(ex); + throw; + } + finally + { + scope.Dispose(); + } + } + + /// + /// The + /// operation downloads a file from the service, including its metadata + /// and properties. + /// + /// For more information, see + /// + /// Get Blob. + /// + /// + /// A describing the + /// downloaded file. contains + /// the file's data. + /// + /// + /// A will be thrown if + /// a failure occurs. + /// + public virtual async Task> ReadStreamingAsync() + { + DiagnosticScope scope = ClientConfiguration.ClientDiagnostics.CreateScope($"{nameof(DataLakeFileClient)}.{nameof(ReadStreaming)}"); + + try + { + scope.Start(); + + Response response = await _blockBlobClient.DownloadStreamingAsync() + .ConfigureAwait(false); + + return Response.FromValue( + response.ToDataLakeFileReadStreamingResult(), + response.GetRawResponse()); + } + catch (Exception ex) + { + scope.Failed(ex); + throw; + } + finally + { + scope.Dispose(); + } + } + + /// + /// The + /// operation downloads a file from the service, including its metadata + /// and properties. + /// + /// For more information, see + /// + /// Get Blob. + /// + /// + /// Optional to propagate + /// notifications that the operation should be cancelled. + /// + /// + /// A describing the + /// downloaded file. contains + /// the file's data. + /// + /// + /// A will be thrown if + /// a failure occurs. + /// + public virtual Response ReadStreaming( + CancellationToken cancellationToken) + { + DiagnosticScope scope = ClientConfiguration.ClientDiagnostics.CreateScope($"{nameof(DataLakeFileClient)}.{nameof(ReadStreaming)}"); + + try + { + scope.Start(); + + Response response = _blockBlobClient.DownloadStreaming( + cancellationToken: cancellationToken); + + return Response.FromValue( + response.ToDataLakeFileReadStreamingResult(), + response.GetRawResponse()); + } + catch (Exception ex) + { + scope.Failed(ex); + throw; + } + finally + { + scope.Dispose(); + } + } + + /// + /// The + /// operation downloads a file from the service, including its metadata + /// and properties. + /// + /// For more information, see + /// + /// Get Blob. + /// + /// + /// Optional to propagate + /// notifications that the operation should be cancelled. + /// + /// + /// A describing the + /// downloaded file. contains + /// the file's data. + /// + /// + /// A will be thrown if + /// a failure occurs. + /// + public virtual async Task> ReadStreamingAsync( + CancellationToken cancellationToken) + { + DiagnosticScope scope = ClientConfiguration.ClientDiagnostics.CreateScope($"{nameof(DataLakeFileClient)}.{nameof(ReadStreaming)}"); + + try + { + scope.Start(); + + Response response = await _blockBlobClient.DownloadStreamingAsync( + cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return Response.FromValue( + response.ToDataLakeFileReadStreamingResult(), + response.GetRawResponse()); + } + catch (Exception ex) + { + scope.Failed(ex); + throw; + } + finally + { + scope.Dispose(); + } + } + + /// + /// The + /// operation downloads a file from the service, including its metadata + /// and properties. + /// + /// For more information, see + /// + /// Get Blob. + /// + /// + /// Optional parameters. + /// + /// + /// Optional to propagate + /// notifications that the operation should be cancelled. + /// + /// + /// A describing the + /// downloaded file. contains + /// the file's data. + /// + /// + /// A will be thrown if + /// a failure occurs. + /// + public virtual Response ReadStreaming( + DataLakeFileReadOptions options, + CancellationToken cancellationToken = default) + { + DiagnosticScope scope = ClientConfiguration.ClientDiagnostics.CreateScope($"{nameof(DataLakeFileClient)}.{nameof(ReadStreaming)}"); + + try + { + scope.Start(); + + Response response = _blockBlobClient.DownloadStreaming( + options: options.ToBlobBaseDownloadOptions(), + cancellationToken: cancellationToken); + + return Response.FromValue( + response.ToDataLakeFileReadStreamingResult(), + response.GetRawResponse()); + } + catch (Exception ex) + { + scope.Failed(ex); + throw; + } + finally + { + scope.Dispose(); + } + } + + /// + /// The + /// operation downloads a file from the service, including its metadata + /// and properties. + /// + /// For more information, see + /// + /// Get Blob. + /// + /// + /// Optional parameters. + /// + /// + /// Optional to propagate + /// notifications that the operation should be cancelled. + /// + /// + /// A describing the + /// downloaded file. contains + /// the file's data. + /// + /// + /// A will be thrown if + /// a failure occurs. + /// + public virtual async Task> ReadStreamingAsync( + DataLakeFileReadOptions options, + CancellationToken cancellationToken = default) + { + DiagnosticScope scope = ClientConfiguration.ClientDiagnostics.CreateScope($"{nameof(DataLakeFileClient)}.{nameof(ReadStreaming)}"); + + try + { + scope.Start(); + + Response response = await _blockBlobClient.DownloadStreamingAsync( + options: options.ToBlobBaseDownloadOptions(), + cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return Response.FromValue( + response.ToDataLakeFileReadStreamingResult(), + response.GetRawResponse()); + } + catch (Exception ex) + { + scope.Failed(ex); + throw; + } + finally + { + scope.Dispose(); + } + } + + /// + /// The + /// operation downloads a file from the service, including its metadata + /// and properties. + /// + /// For more information, see + /// + /// Get Blob. + /// + /// + /// A describing the + /// downloaded file. contains + /// the file's data. + /// + /// + /// A will be thrown if + /// a failure occurs. + /// + public virtual Response ReadContent() + { + DiagnosticScope scope = ClientConfiguration.ClientDiagnostics.CreateScope($"{nameof(DataLakeFileClient)}.{nameof(ReadContent)}"); + + try + { + scope.Start(); + + Response response = _blockBlobClient.DownloadContent(); + + return Response.FromValue( + response.ToDataLakeFileReadResult(), + response.GetRawResponse()); + } + catch (Exception ex) + { + scope.Failed(ex); + throw; + } + finally + { + scope.Dispose(); + } + } + + /// + /// The + /// operation downloads a file from the service, including its metadata + /// and properties. + /// + /// For more information, see + /// + /// Get Blob. + /// + /// + /// A describing the + /// downloaded file. contains + /// the file's data. + /// + /// + /// A will be thrown if + /// a failure occurs. + /// + public virtual async Task> ReadContentAsync() + { + DiagnosticScope scope = ClientConfiguration.ClientDiagnostics.CreateScope($"{nameof(DataLakeFileClient)}.{nameof(ReadContent)}"); + + try + { + scope.Start(); + + Response response = await _blockBlobClient.DownloadContentAsync() + .ConfigureAwait(false); + + return Response.FromValue( + response.ToDataLakeFileReadResult(), + response.GetRawResponse()); + } + catch (Exception ex) + { + scope.Failed(ex); + throw; + } + finally + { + scope.Dispose(); + } + } + + /// + /// The + /// operation downloads a file from the service, including its metadata + /// and properties. + /// + /// For more information, see + /// + /// Get Blob. + /// + /// + /// Optional to propagate + /// notifications that the operation should be cancelled. + /// + /// + /// A describing the + /// downloaded file. contains + /// the file's data. + /// + /// + /// A will be thrown if + /// a failure occurs. + /// + public virtual Response ReadContent( + CancellationToken cancellationToken) + { + DiagnosticScope scope = ClientConfiguration.ClientDiagnostics.CreateScope($"{nameof(DataLakeFileClient)}.{nameof(ReadContent)}"); + + try + { + scope.Start(); + + Response response = _blockBlobClient.DownloadContent( + cancellationToken: cancellationToken); + + return Response.FromValue( + response.ToDataLakeFileReadResult(), + response.GetRawResponse()); + } + catch (Exception ex) + { + scope.Failed(ex); + throw; + } + finally + { + scope.Dispose(); + } + } + + /// + /// The + /// operation downloads a file from the service, including its metadata + /// and properties. + /// + /// For more information, see + /// + /// Get Blob. + /// + /// + /// Optional to propagate + /// notifications that the operation should be cancelled. + /// + /// + /// A describing the + /// downloaded file. contains + /// the file's data. + /// + /// + /// A will be thrown if + /// a failure occurs. + /// + public virtual async Task> ReadContentAsync( + CancellationToken cancellationToken) + { + DiagnosticScope scope = ClientConfiguration.ClientDiagnostics.CreateScope($"{nameof(DataLakeFileClient)}.{nameof(ReadContent)}"); + + try + { + scope.Start(); + + Response response = await _blockBlobClient.DownloadContentAsync( + cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return Response.FromValue( + response.ToDataLakeFileReadResult(), + response.GetRawResponse()); + } + catch (Exception ex) + { + scope.Failed(ex); + throw; + } + finally + { + scope.Dispose(); + } + } + + /// + /// The + /// operation downloads a file from the service, including its metadata + /// and properties. + /// + /// For more information, see + /// + /// Get Blob. + /// + /// + /// Optional parameters. + /// + /// + /// Optional to propagate + /// notifications that the operation should be cancelled. + /// + /// + /// A describing the + /// downloaded file. contains + /// the file's data. + /// + /// + /// A will be thrown if + /// a failure occurs. + /// + public virtual Response ReadContent( + DataLakeFileReadOptions options, + CancellationToken cancellationToken = default) + { + DiagnosticScope scope = ClientConfiguration.ClientDiagnostics.CreateScope($"{nameof(DataLakeFileClient)}.{nameof(ReadContent)}"); + + try + { + scope.Start(); + + Response response = _blockBlobClient.DownloadContent( + options: options.ToBlobBaseDownloadOptions(), + cancellationToken: cancellationToken); + + return Response.FromValue( + response.ToDataLakeFileReadResult(), + response.GetRawResponse()); + } + catch (Exception ex) + { + scope.Failed(ex); + throw; + } + finally + { + scope.Dispose(); + } + } + + /// + /// The + /// operation downloads a file from the service, including its metadata + /// and properties. + /// + /// For more information, see + /// + /// Get Blob. + /// + /// + /// Optional parameters. + /// + /// + /// Optional to propagate + /// notifications that the operation should be cancelled. + /// + /// + /// A describing the + /// downloaded file. contains + /// the file's data. + /// + /// + /// A will be thrown if + /// a failure occurs. + /// + public virtual async Task> ReadContentAsync( + DataLakeFileReadOptions options, + CancellationToken cancellationToken = default) + { + DiagnosticScope scope = ClientConfiguration.ClientDiagnostics.CreateScope($"{nameof(DataLakeFileClient)}.{nameof(ReadContent)}"); + + try + { + scope.Start(); + + Response response = await _blockBlobClient.DownloadContentAsync( + options: options.ToBlobBaseDownloadOptions(), + cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return Response.FromValue( + response.ToDataLakeFileReadResult(), + response.GetRawResponse()); + } + catch (Exception ex) + { + scope.Failed(ex); + throw; + } + finally + { + scope.Dispose(); + } + } #endregion Read Data #region Read To diff --git a/sdk/storage/Azure.Storage.Files.DataLake/src/DataLakeFileSystemClient.cs b/sdk/storage/Azure.Storage.Files.DataLake/src/DataLakeFileSystemClient.cs index 96cccd792d8cc..5a5e9ca8650cc 100644 --- a/sdk/storage/Azure.Storage.Files.DataLake/src/DataLakeFileSystemClient.cs +++ b/sdk/storage/Azure.Storage.Files.DataLake/src/DataLakeFileSystemClient.cs @@ -3287,26 +3287,158 @@ public virtual Uri GenerateSasUri( // Deep copy of builder so we don't modify the user's original DataLakeSasBuilder. builder = DataLakeSasBuilder.DeepCopy(builder); - // Assign builder's FileSystemName, if it is null. - builder.FileSystemName ??= Name; - - if (!builder.FileSystemName.Equals(Name, StringComparison.InvariantCulture)) + SetBuilderAndValidate(builder); + DataLakeUriBuilder sasUri = new DataLakeUriBuilder(Uri) { - throw Errors.SasNamesNotMatching( - nameof(builder.FileSystemName), - nameof(DataLakeSasBuilder), - nameof(Name)); - } - if (!string.IsNullOrEmpty(builder.Path)) + Sas = builder.ToSasQueryParameters(ClientConfiguration.SharedKeyCredential, out stringToSign) + }; + return sasUri.ToUri(); + } + #endregion + + #region GenerateUserDelegationSas + /// + /// The + /// returns a that generates a DataLake FileSystem Service + /// Shared Access Signature (SAS) Uri based on the Client properties and parameters passed. + /// The SAS is signed by the user delegation key passed in. + /// + /// For more information, see + /// + /// Creating an user delegation SAS. + /// + /// + /// Required. Specifies the list of permissions to be associated with the SAS. + /// See . + /// + /// + /// Required. Specifies the time at which the SAS becomes invalid. This field + /// must be omitted if it has been specified in an associated stored access policy. + /// + /// + /// Required. A returned from + /// . + /// + /// + /// A containing the SAS Uri. + /// + /// + /// A will be thrown if a failure occurs. + /// + [CallerShouldAudit("https://aka.ms/azsdk/callershouldaudit/storage-files-datalake")] + public virtual Uri GenerateUserDelegationSasUri(DataLakeFileSystemSasPermissions permissions, DateTimeOffset expiresOn, Models.UserDelegationKey userDelegationKey) => + GenerateUserDelegationSasUri(permissions, expiresOn, userDelegationKey, out _); + + /// + /// The + /// returns a that generates a DataLake FileSystem Service + /// Shared Access Signature (SAS) Uri based on the Client properties and parameters passed. + /// The SAS is signed by the user delegation key passed in. + /// + /// For more information, see + /// + /// Creating an user delegation SAS. + /// + /// + /// Required. Specifies the list of permissions to be associated with the SAS. + /// See . + /// + /// + /// Required. Specifies the time at which the SAS becomes invalid. This field + /// must be omitted if it has been specified in an associated stored access policy. + /// + /// + /// Required. A returned from + /// . + /// + /// + /// For debugging purposes only. This string will be overwritten with the string to sign that was used to generate the SAS Uri. + /// + /// + /// A containing the SAS Uri. + /// + /// + /// A will be thrown if a failure occurs. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + [CallerShouldAudit("https://aka.ms/azsdk/callershouldaudit/storage-files-datalake")] + public virtual Uri GenerateUserDelegationSasUri(DataLakeFileSystemSasPermissions permissions, DateTimeOffset expiresOn, Models.UserDelegationKey userDelegationKey, out string stringToSign) => + GenerateUserDelegationSasUri(new DataLakeSasBuilder(permissions, expiresOn) { FileSystemName = Name }, userDelegationKey, out stringToSign); + + /// + /// The returns a + /// that generates a DataLake FileSystem Service Shared Access Signature (SAS) + /// Uri based on the Client properties and builder passed. + /// The SAS is signed by the user delegation key passed in. + /// + /// For more information, see + /// + /// Creating an user delegation SAS. + /// + /// + /// Required. Used to generate a Shared Access Signature (SAS). + /// + /// + /// Required. A returned from + /// . + /// + /// + /// A on successfully deleting. + /// + /// + /// A will be thrown if + /// a failure occurs. + /// + [CallerShouldAudit("https://aka.ms/azsdk/callershouldaudit/storage-files-datalake")] + public virtual Uri GenerateUserDelegationSasUri(DataLakeSasBuilder builder, Models.UserDelegationKey userDelegationKey) + => GenerateUserDelegationSasUri(builder, userDelegationKey, out _); + + /// + /// The returns a + /// that generates a DataLake FileSystem Service Shared Access Signature (SAS) + /// Uri based on the Client properties and builder passed. + /// The SAS is signed by the user delegation key passed in. + /// + /// For more information, see + /// + /// Creating an user delegation SAS. + /// + /// + /// Required. Used to generate a Shared Access Signature (SAS). + /// + /// + /// Required. A returned from + /// . + /// + /// + /// For debugging purposes only. This string will be overwritten with the string to sign that was used to generate the SAS Uri. + /// + /// + /// A on successfully deleting. + /// + /// + /// A will be thrown if + /// a failure occurs. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + [CallerShouldAudit("https://aka.ms/azsdk/callershouldaudit/storage-files-datalake")] + public virtual Uri GenerateUserDelegationSasUri(DataLakeSasBuilder builder, Models.UserDelegationKey userDelegationKey, out string stringToSign) + { + builder = builder ?? throw Errors.ArgumentNull(nameof(builder)); + userDelegationKey = userDelegationKey ?? throw Errors.ArgumentNull(nameof(userDelegationKey)); + + // Deep copy of builder so we don't modify the user's original DataLakeSasBuilder. + builder = DataLakeSasBuilder.DeepCopy(builder); + + SetBuilderAndValidate(builder); + if (string.IsNullOrEmpty(AccountName)) { - throw Errors.SasBuilderEmptyParam( - nameof(builder), - nameof(builder.Path), - nameof(Constants.DataLake.FileSystemName)); + throw Errors.SasClientMissingData(nameof(AccountName)); } + DataLakeUriBuilder sasUri = new DataLakeUriBuilder(Uri) { - Sas = builder.ToSasQueryParameters(ClientConfiguration.SharedKeyCredential, out stringToSign) + Sas = builder.ToSasQueryParameters(userDelegationKey, AccountName, out stringToSign) }; return sasUri.ToUri(); } @@ -3622,6 +3754,28 @@ protected internal virtual DataLakeServiceClient GetParentServiceClientCore() return _parentServiceClient; } #endregion + + private void SetBuilderAndValidate(DataLakeSasBuilder builder) + { + // Assign builder's FileSystemName, if it is null. + builder.FileSystemName ??= Name; + + // Validate that builder is properly set + if (!builder.FileSystemName.Equals(Name, StringComparison.InvariantCulture)) + { + throw Errors.SasNamesNotMatching( + nameof(builder.FileSystemName), + nameof(DataLakeSasBuilder), + nameof(Name)); + } + if (!string.IsNullOrEmpty(builder.Path)) + { + throw Errors.SasBuilderEmptyParam( + nameof(builder), + nameof(builder.Path), + nameof(Constants.DataLake.FileSystemName)); + } + } } namespace Specialized diff --git a/sdk/storage/Azure.Storage.Files.DataLake/src/DataLakePathClient.cs b/sdk/storage/Azure.Storage.Files.DataLake/src/DataLakePathClient.cs index c876715c2f7b0..cd7d7e0ec75d3 100644 --- a/sdk/storage/Azure.Storage.Files.DataLake/src/DataLakePathClient.cs +++ b/sdk/storage/Azure.Storage.Files.DataLake/src/DataLakePathClient.cs @@ -529,7 +529,7 @@ internal DataLakePathClient( (PathRestClient dfsPathRestClient, PathRestClient blobPathRestClient) = BuildPathRestClients(_dfsUri, _blobUri); _pathRestClient = dfsPathRestClient; - _blobPathRestClient = blobPathRestClient; + _blobPathRestClient = blobPathRestClient; DataLakeErrors.VerifyHttpsCustomerProvidedKey(_uri, _clientConfiguration.CustomerProvidedKey); } @@ -1166,7 +1166,7 @@ internal virtual async Task> CreateInternal( if (expiresOn.HasValue && timeToExpire.HasValue) { - throw new ArgumentException($"{nameof(DataLakePathCreateOptions)}.{nameof(DataLakePathCreateOptions.ScheduleDeletionOptions.ExpiresOn)} and {nameof(DataLakePathCreateOptions)}.{nameof(DataLakePathCreateOptions.ScheduleDeletionOptions.TimeToExpire)} cannot both be set."); + throw new ArgumentException($"{nameof(DataLakePathCreateOptions)}.{nameof(DataLakePathCreateOptions.ScheduleDeletionOptions.ExpiresOn)} and {nameof(DataLakePathCreateOptions)}.{nameof(DataLakePathCreateOptions.ScheduleDeletionOptions.TimeToExpire)} cannot both be set."); } try @@ -1418,10 +1418,10 @@ public virtual async Task> CreateIfNotExistsAsync( public virtual Response CreateIfNotExists( #pragma warning restore AZC0002 // DO ensure all service methods, both asynchronous and synchronous, take an optional CancellationToken parameter called cancellationToken. PathResourceType resourceType, - PathHttpHeaders httpHeaders , + PathHttpHeaders httpHeaders, Metadata metadata, string permissions, - string umask , + string umask, CancellationToken cancellationToken) => CreateIfNotExistsInternal( resourceType: resourceType, @@ -3946,36 +3946,158 @@ public virtual Uri GenerateSasUri(DataLakeSasBuilder builder, out string stringT // Deep copy of builder so we don't modify the user's original DataLakeSasBuilder. builder = DataLakeSasBuilder.DeepCopy(builder); - // Assign builder's IsDirectory, FileSystemName, and Path, if they are null. - builder.IsDirectory ??= GetType() == typeof(DataLakeDirectoryClient); - builder.FileSystemName ??= FileSystemName; - builder.Path ??= Path; - - if (builder.IsDirectory.GetValueOrDefault(false)) + SetBuilderAndValidate(builder); + DataLakeUriBuilder sasUri = new DataLakeUriBuilder(Uri) { - throw Errors.SasIncorrectResourceType( - nameof(builder), - nameof(builder.IsDirectory), - nameof(Constants.FalseName), - nameof(this.GetType)); - } - if (!builder.FileSystemName.Equals(FileSystemName, StringComparison.InvariantCulture)) + Sas = builder.ToSasQueryParameters(ClientConfiguration.SharedKeyCredential, out stringToSign) + }; + return sasUri.ToUri(); + } + #endregion + + #region GenerateUserDelegationSas + /// + /// The + /// returns a that generates a DataLake Path Service Shared Access Signature (SAS) + /// Uri based on the Client properties and parameter passed. The SAS is signed by the user delegation key passed in. + /// + /// For more information, see + /// + /// Creating an user delegation SAS. + /// + /// + /// Required. Specifies the list of permissions to be associated with the SAS. + /// See . + /// + /// + /// Required. Specifies the time at which the SAS becomes invalid. This field + /// must be omitted if it has been specified in an associated stored access policy. + /// + /// + /// Required. A returned from + /// . + /// + /// + /// A containing the SAS Uri. + /// + /// + /// A will be thrown if a failure occurs. + /// + [CallerShouldAudit("https://aka.ms/azsdk/callershouldaudit/storage-files-datalake")] + public virtual Uri GenerateUserDelegationSasUri(DataLakeSasPermissions permissions, DateTimeOffset expiresOn, UserDelegationKey userDelegationKey) + => GenerateUserDelegationSasUri(permissions, expiresOn, userDelegationKey, out _); + + /// + /// The + /// returns a that generates a DataLake Path Service Shared Access Signature (SAS) + /// Uri based on the Client properties and parameter passed. The SAS is signed by the user delegation key passed in. + /// + /// For more information, see + /// + /// Creating an user delegation SAS. + /// + /// + /// Required. Specifies the list of permissions to be associated with the SAS. + /// See . + /// + /// + /// Required. Specifies the time at which the SAS becomes invalid. This field + /// must be omitted if it has been specified in an associated stored access policy. + /// + /// + /// Required. A returned from + /// . + /// + /// + /// For debugging purposes only. This string will be overwritten with the string to sign that was used to generate the SAS Uri. + /// + /// + /// A containing the SAS Uri. + /// + /// + /// A will be thrown if a failure occurs. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + [CallerShouldAudit("https://aka.ms/azsdk/callershouldaudit/storage-files-datalake")] + public virtual Uri GenerateUserDelegationSasUri(DataLakeSasPermissions permissions, DateTimeOffset expiresOn, UserDelegationKey userDelegationKey, out string stringToSign) => + GenerateUserDelegationSasUri(new DataLakeSasBuilder(permissions, expiresOn) { - throw Errors.SasNamesNotMatching( - nameof(builder.FileSystemName), - nameof(DataLakeSasBuilder), - nameof(FileSystemName)); - } - if (!builder.Path.Equals(Path, StringComparison.InvariantCulture)) + FileSystemName = FileSystemName, + Path = Path + }, userDelegationKey, out stringToSign); + + /// + /// The + /// returns a that generates a DataLake Path Service Shared Access Signature (SAS) + /// Uri based on the Client properties and builder passed. The SAS is signed by the user delegation key passed in. + /// + /// For more information, see + /// + /// Creating an user delegation SAS. + /// + /// + /// Required. Used to generate a Shared Access Signature (SAS). + /// + /// + /// Required. A returned from + /// . + /// + /// + /// A containing the SAS Uri. + /// + /// + /// A will be thrown if + /// a failure occurs. + /// + [CallerShouldAudit("https://aka.ms/azsdk/callershouldaudit/storage-files-datalake")] + public virtual Uri GenerateUserDelegationSasUri(DataLakeSasBuilder builder, UserDelegationKey userDelegationKey) + => GenerateUserDelegationSasUri(builder, userDelegationKey, out _); + + /// + /// The + /// returns a that generates a DataLake Path Service Shared Access Signature (SAS) + /// Uri based on the Client properties and builder passed. The SAS is signed by the user delegation key passed in. + /// + /// For more information, see + /// + /// Creating an user delegation SAS. + /// + /// + /// Required. Used to generate a Shared Access Signature (SAS). + /// + /// + /// Required. A returned from + /// . + /// + /// + /// For debugging purposes only. This string will be overwritten with the string to sign that was used to generate the SAS Uri. + /// + /// + /// A containing the SAS Uri. + /// + /// + /// A will be thrown if + /// a failure occurs. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + [CallerShouldAudit("https://aka.ms/azsdk/callershouldaudit/storage-files-datalake")] + public virtual Uri GenerateUserDelegationSasUri(DataLakeSasBuilder builder, UserDelegationKey userDelegationKey, out string stringToSign) + { + builder = builder ?? throw Errors.ArgumentNull(nameof(builder)); + userDelegationKey = userDelegationKey ?? throw Errors.ArgumentNull(nameof(userDelegationKey)); + + // Deep copy of builder so we don't modify the user's original DataLakeSasBuilder. + builder = DataLakeSasBuilder.DeepCopy(builder); + + SetBuilderAndValidate(builder); + if (string.IsNullOrEmpty(AccountName)) { - throw Errors.SasNamesNotMatching( - nameof(builder.Path), - nameof(DataLakeSasBuilder), - nameof(Path)); + throw Errors.SasClientMissingData(nameof(AccountName)); } + DataLakeUriBuilder sasUri = new DataLakeUriBuilder(Uri) { - Sas = builder.ToSasQueryParameters(ClientConfiguration.SharedKeyCredential, out stringToSign) + Sas = builder.ToSasQueryParameters(userDelegationKey, AccountName, out stringToSign) }; return sasUri.ToUri(); } @@ -4042,6 +4164,38 @@ protected internal virtual DataLakeDirectoryClient GetParentDirectoryClientCore( return _parentDirectoryClient; } #endregion + + private void SetBuilderAndValidate(DataLakeSasBuilder builder) + { + // Assign builder's IsDirectory, FileSystemName, and Path, if they are null. + builder.IsDirectory ??= GetType() == typeof(DataLakeDirectoryClient); + builder.FileSystemName ??= FileSystemName; + builder.Path ??= Path; + + // Validate that builder is properly set + if (builder.IsDirectory.GetValueOrDefault(false)) + { + throw Errors.SasIncorrectResourceType( + nameof(builder), + nameof(builder.IsDirectory), + Constants.FalseName, + nameof(this.GetType)); + } + if (!builder.FileSystemName.Equals(FileSystemName, StringComparison.InvariantCulture)) + { + throw Errors.SasNamesNotMatching( + nameof(builder.FileSystemName), + nameof(DataLakeSasBuilder), + nameof(FileSystemName)); + } + if (!builder.Path.Equals(Path, StringComparison.InvariantCulture)) + { + throw Errors.SasNamesNotMatching( + nameof(builder.Path), + nameof(DataLakeSasBuilder), + nameof(Path)); + } + } } namespace Specialized diff --git a/sdk/storage/Azure.Storage.Files.DataLake/src/Generated/FileSystemRestClient.cs b/sdk/storage/Azure.Storage.Files.DataLake/src/Generated/FileSystemRestClient.cs index 719932d5cd500..4144d908b7549 100644 --- a/sdk/storage/Azure.Storage.Files.DataLake/src/Generated/FileSystemRestClient.cs +++ b/sdk/storage/Azure.Storage.Files.DataLake/src/Generated/FileSystemRestClient.cs @@ -33,7 +33,7 @@ internal partial class FileSystemRestClient /// The HTTP pipeline for sending and receiving REST requests and responses. /// The URL of the service account, container, or blob that is the target of the desired operation. /// The value must be "filesystem" for all filesystem operations. The default value is "filesystem". - /// Specifies the version of the operation to use for this request. The default value is "2023-05-03". + /// Specifies the version of the operation to use for this request. The default value is "2025-01-05". /// , , , or is null. public FileSystemRestClient(ClientDiagnostics clientDiagnostics, HttpPipeline pipeline, string url, string resource, string version) { diff --git a/sdk/storage/Azure.Storage.Files.DataLake/src/Generated/PathAppendDataHeaders.cs b/sdk/storage/Azure.Storage.Files.DataLake/src/Generated/PathAppendDataHeaders.cs index 6ec456a438564..502dd557f4822 100644 --- a/sdk/storage/Azure.Storage.Files.DataLake/src/Generated/PathAppendDataHeaders.cs +++ b/sdk/storage/Azure.Storage.Files.DataLake/src/Generated/PathAppendDataHeaders.cs @@ -29,5 +29,7 @@ public PathAppendDataHeaders(Response response) public string EncryptionKeySha256 => _response.Headers.TryGetValue("x-ms-encryption-key-sha256", out string value) ? value : null; /// If the lease was auto-renewed with this request. public bool? LeaseRenewed => _response.Headers.TryGetValue("x-ms-lease-renewed", out bool? value) ? value : null; + /// Indicates the structured message body was accepted and mirrors back the message schema version and properties. + public string StructuredBodyType => _response.Headers.TryGetValue("x-ms-structured-body", out string value) ? value : null; } } diff --git a/sdk/storage/Azure.Storage.Files.DataLake/src/Generated/PathRestClient.cs b/sdk/storage/Azure.Storage.Files.DataLake/src/Generated/PathRestClient.cs index 6b1e970bd2fc8..d328c3079de6b 100644 --- a/sdk/storage/Azure.Storage.Files.DataLake/src/Generated/PathRestClient.cs +++ b/sdk/storage/Azure.Storage.Files.DataLake/src/Generated/PathRestClient.cs @@ -30,7 +30,7 @@ internal partial class PathRestClient /// The handler for diagnostic messaging in the client. /// The HTTP pipeline for sending and receiving REST requests and responses. /// The URL of the service account, container, or blob that is the target of the desired operation. - /// Specifies the version of the operation to use for this request. The default value is "2023-05-03". + /// Specifies the version of the operation to use for this request. The default value is "2025-01-05". /// The lease duration is required to acquire a lease, and specifies the duration of the lease in seconds. The lease duration must be between 15 and 60 seconds or -1 for infinite lease. /// , , or is null. public PathRestClient(ClientDiagnostics clientDiagnostics, HttpPipeline pipeline, string url, string version, int? xMsLeaseDuration = null) @@ -293,7 +293,7 @@ public ResponseWithHeaders Create(int? timeout = null, PathRe } } - internal HttpMessage CreateUpdateRequest(PathUpdateAction action, PathSetAccessControlRecursiveMode mode, Stream body, int? timeout, int? maxRecords, string continuation, bool? forceFlag, long? position, bool? retainUncommittedData, bool? close, long? contentLength, byte[] contentMD5, string leaseId, string cacheControl, string contentType, string contentDisposition, string contentEncoding, string contentLanguage, string properties, string owner, string group, string permissions, string acl, string ifMatch, string ifNoneMatch, DateTimeOffset? ifModifiedSince, DateTimeOffset? ifUnmodifiedSince) + internal HttpMessage CreateUpdateRequest(PathUpdateAction action, PathSetAccessControlRecursiveMode mode, Stream body, int? timeout, int? maxRecords, string continuation, bool? forceFlag, long? position, bool? retainUncommittedData, bool? close, long? contentLength, byte[] contentMD5, string leaseId, string cacheControl, string contentType, string contentDisposition, string contentEncoding, string contentLanguage, string properties, string owner, string group, string permissions, string acl, string ifMatch, string ifNoneMatch, DateTimeOffset? ifModifiedSince, DateTimeOffset? ifUnmodifiedSince, string structuredBodyType, long? structuredContentLength) { var message = _pipeline.CreateMessage(); var request = message.Request; @@ -396,6 +396,14 @@ internal HttpMessage CreateUpdateRequest(PathUpdateAction action, PathSetAccessC { request.Headers.Add("If-Unmodified-Since", ifUnmodifiedSince.Value, "R"); } + if (structuredBodyType != null) + { + request.Headers.Add("x-ms-structured-body", structuredBodyType); + } + if (structuredContentLength != null) + { + request.Headers.Add("x-ms-structured-content-length", structuredContentLength.Value); + } request.Headers.Add("Accept", "application/json"); if (contentLength != null) { @@ -434,17 +442,19 @@ internal HttpMessage CreateUpdateRequest(PathUpdateAction action, PathSetAccessC /// Specify an ETag value to operate only on blobs without a matching value. /// Specify this header value to operate only on a blob if it has been modified since the specified date/time. /// Specify this header value to operate only on a blob if it has not been modified since the specified date/time. + /// Required if the request body is a structured message. Specifies the message schema version and properties. + /// Required if the request body is a structured message. Specifies the length of the blob/file content inside the message body. Will always be smaller than Content-Length. /// The cancellation token to use. /// is null. /// Uploads data to be appended to a file, flushes (writes) previously uploaded data to a file, sets properties for a file or directory, or sets access control for a file or directory. Data can only be appended to a file. Concurrent writes to the same file using multiple clients are not supported. This operation supports conditional HTTP requests. For more information, see [Specifying Conditional Headers for Blob Service Operations](https://docs.microsoft.com/en-us/rest/api/storageservices/specifying-conditional-headers-for-blob-service-operations). - public async Task> UpdateAsync(PathUpdateAction action, PathSetAccessControlRecursiveMode mode, Stream body, int? timeout = null, int? maxRecords = null, string continuation = null, bool? forceFlag = null, long? position = null, bool? retainUncommittedData = null, bool? close = null, long? contentLength = null, byte[] contentMD5 = null, string leaseId = null, string cacheControl = null, string contentType = null, string contentDisposition = null, string contentEncoding = null, string contentLanguage = null, string properties = null, string owner = null, string group = null, string permissions = null, string acl = null, string ifMatch = null, string ifNoneMatch = null, DateTimeOffset? ifModifiedSince = null, DateTimeOffset? ifUnmodifiedSince = null, CancellationToken cancellationToken = default) + public async Task> UpdateAsync(PathUpdateAction action, PathSetAccessControlRecursiveMode mode, Stream body, int? timeout = null, int? maxRecords = null, string continuation = null, bool? forceFlag = null, long? position = null, bool? retainUncommittedData = null, bool? close = null, long? contentLength = null, byte[] contentMD5 = null, string leaseId = null, string cacheControl = null, string contentType = null, string contentDisposition = null, string contentEncoding = null, string contentLanguage = null, string properties = null, string owner = null, string group = null, string permissions = null, string acl = null, string ifMatch = null, string ifNoneMatch = null, DateTimeOffset? ifModifiedSince = null, DateTimeOffset? ifUnmodifiedSince = null, string structuredBodyType = null, long? structuredContentLength = null, CancellationToken cancellationToken = default) { if (body == null) { throw new ArgumentNullException(nameof(body)); } - using var message = CreateUpdateRequest(action, mode, body, timeout, maxRecords, continuation, forceFlag, position, retainUncommittedData, close, contentLength, contentMD5, leaseId, cacheControl, contentType, contentDisposition, contentEncoding, contentLanguage, properties, owner, group, permissions, acl, ifMatch, ifNoneMatch, ifModifiedSince, ifUnmodifiedSince); + using var message = CreateUpdateRequest(action, mode, body, timeout, maxRecords, continuation, forceFlag, position, retainUncommittedData, close, contentLength, contentMD5, leaseId, cacheControl, contentType, contentDisposition, contentEncoding, contentLanguage, properties, owner, group, permissions, acl, ifMatch, ifNoneMatch, ifModifiedSince, ifUnmodifiedSince, structuredBodyType, structuredContentLength); await _pipeline.SendAsync(message, cancellationToken).ConfigureAwait(false); var headers = new PathUpdateHeaders(message.Response); switch (message.Response.Status) @@ -491,17 +501,19 @@ public async Task Specify an ETag value to operate only on blobs without a matching value. /// Specify this header value to operate only on a blob if it has been modified since the specified date/time. /// Specify this header value to operate only on a blob if it has not been modified since the specified date/time. + /// Required if the request body is a structured message. Specifies the message schema version and properties. + /// Required if the request body is a structured message. Specifies the length of the blob/file content inside the message body. Will always be smaller than Content-Length. /// The cancellation token to use. /// is null. /// Uploads data to be appended to a file, flushes (writes) previously uploaded data to a file, sets properties for a file or directory, or sets access control for a file or directory. Data can only be appended to a file. Concurrent writes to the same file using multiple clients are not supported. This operation supports conditional HTTP requests. For more information, see [Specifying Conditional Headers for Blob Service Operations](https://docs.microsoft.com/en-us/rest/api/storageservices/specifying-conditional-headers-for-blob-service-operations). - public ResponseWithHeaders Update(PathUpdateAction action, PathSetAccessControlRecursiveMode mode, Stream body, int? timeout = null, int? maxRecords = null, string continuation = null, bool? forceFlag = null, long? position = null, bool? retainUncommittedData = null, bool? close = null, long? contentLength = null, byte[] contentMD5 = null, string leaseId = null, string cacheControl = null, string contentType = null, string contentDisposition = null, string contentEncoding = null, string contentLanguage = null, string properties = null, string owner = null, string group = null, string permissions = null, string acl = null, string ifMatch = null, string ifNoneMatch = null, DateTimeOffset? ifModifiedSince = null, DateTimeOffset? ifUnmodifiedSince = null, CancellationToken cancellationToken = default) + public ResponseWithHeaders Update(PathUpdateAction action, PathSetAccessControlRecursiveMode mode, Stream body, int? timeout = null, int? maxRecords = null, string continuation = null, bool? forceFlag = null, long? position = null, bool? retainUncommittedData = null, bool? close = null, long? contentLength = null, byte[] contentMD5 = null, string leaseId = null, string cacheControl = null, string contentType = null, string contentDisposition = null, string contentEncoding = null, string contentLanguage = null, string properties = null, string owner = null, string group = null, string permissions = null, string acl = null, string ifMatch = null, string ifNoneMatch = null, DateTimeOffset? ifModifiedSince = null, DateTimeOffset? ifUnmodifiedSince = null, string structuredBodyType = null, long? structuredContentLength = null, CancellationToken cancellationToken = default) { if (body == null) { throw new ArgumentNullException(nameof(body)); } - using var message = CreateUpdateRequest(action, mode, body, timeout, maxRecords, continuation, forceFlag, position, retainUncommittedData, close, contentLength, contentMD5, leaseId, cacheControl, contentType, contentDisposition, contentEncoding, contentLanguage, properties, owner, group, permissions, acl, ifMatch, ifNoneMatch, ifModifiedSince, ifUnmodifiedSince); + using var message = CreateUpdateRequest(action, mode, body, timeout, maxRecords, continuation, forceFlag, position, retainUncommittedData, close, contentLength, contentMD5, leaseId, cacheControl, contentType, contentDisposition, contentEncoding, contentLanguage, properties, owner, group, permissions, acl, ifMatch, ifNoneMatch, ifModifiedSince, ifUnmodifiedSince, structuredBodyType, structuredContentLength); _pipeline.Send(message, cancellationToken); var headers = new PathUpdateHeaders(message.Response); switch (message.Response.Status) @@ -1315,7 +1327,7 @@ public ResponseWithHeaders FlushData(int? timeout = null, } } - internal HttpMessage CreateAppendDataRequest(Stream body, long? position, int? timeout, long? contentLength, byte[] transactionalContentHash, byte[] transactionalContentCrc64, string leaseId, DataLakeLeaseAction? leaseAction, long? leaseDuration, string proposedLeaseId, string encryptionKey, string encryptionKeySha256, EncryptionAlgorithmTypeInternal? encryptionAlgorithm, bool? flush) + internal HttpMessage CreateAppendDataRequest(Stream body, long? position, int? timeout, long? contentLength, byte[] transactionalContentHash, byte[] transactionalContentCrc64, string leaseId, DataLakeLeaseAction? leaseAction, long? leaseDuration, string proposedLeaseId, string encryptionKey, string encryptionKeySha256, EncryptionAlgorithmTypeInternal? encryptionAlgorithm, bool? flush, string structuredBodyType, long? structuredContentLength) { var message = _pipeline.CreateMessage(); var request = message.Request; @@ -1369,6 +1381,14 @@ internal HttpMessage CreateAppendDataRequest(Stream body, long? position, int? t { request.Headers.Add("x-ms-encryption-algorithm", encryptionAlgorithm.Value.ToSerialString()); } + if (structuredBodyType != null) + { + request.Headers.Add("x-ms-structured-body", structuredBodyType); + } + if (structuredContentLength != null) + { + request.Headers.Add("x-ms-structured-content-length", structuredContentLength.Value); + } request.Headers.Add("Accept", "application/json"); if (contentLength != null) { @@ -1398,16 +1418,18 @@ internal HttpMessage CreateAppendDataRequest(Stream body, long? position, int? t /// The SHA-256 hash of the provided encryption key. Must be provided if the x-ms-encryption-key header is provided. /// The algorithm used to produce the encryption key hash. Currently, the only accepted value is "AES256". Must be provided if the x-ms-encryption-key header is provided. /// If file should be flushed after the append. + /// Required if the request body is a structured message. Specifies the message schema version and properties. + /// Required if the request body is a structured message. Specifies the length of the blob/file content inside the message body. Will always be smaller than Content-Length. /// The cancellation token to use. /// is null. - public async Task> AppendDataAsync(Stream body, long? position = null, int? timeout = null, long? contentLength = null, byte[] transactionalContentHash = null, byte[] transactionalContentCrc64 = null, string leaseId = null, DataLakeLeaseAction? leaseAction = null, long? leaseDuration = null, string proposedLeaseId = null, string encryptionKey = null, string encryptionKeySha256 = null, EncryptionAlgorithmTypeInternal? encryptionAlgorithm = null, bool? flush = null, CancellationToken cancellationToken = default) + public async Task> AppendDataAsync(Stream body, long? position = null, int? timeout = null, long? contentLength = null, byte[] transactionalContentHash = null, byte[] transactionalContentCrc64 = null, string leaseId = null, DataLakeLeaseAction? leaseAction = null, long? leaseDuration = null, string proposedLeaseId = null, string encryptionKey = null, string encryptionKeySha256 = null, EncryptionAlgorithmTypeInternal? encryptionAlgorithm = null, bool? flush = null, string structuredBodyType = null, long? structuredContentLength = null, CancellationToken cancellationToken = default) { if (body == null) { throw new ArgumentNullException(nameof(body)); } - using var message = CreateAppendDataRequest(body, position, timeout, contentLength, transactionalContentHash, transactionalContentCrc64, leaseId, leaseAction, leaseDuration, proposedLeaseId, encryptionKey, encryptionKeySha256, encryptionAlgorithm, flush); + using var message = CreateAppendDataRequest(body, position, timeout, contentLength, transactionalContentHash, transactionalContentCrc64, leaseId, leaseAction, leaseDuration, proposedLeaseId, encryptionKey, encryptionKeySha256, encryptionAlgorithm, flush, structuredBodyType, structuredContentLength); await _pipeline.SendAsync(message, cancellationToken).ConfigureAwait(false); var headers = new PathAppendDataHeaders(message.Response); switch (message.Response.Status) @@ -1434,16 +1456,18 @@ public async Task> AppendDataAsync(St /// The SHA-256 hash of the provided encryption key. Must be provided if the x-ms-encryption-key header is provided. /// The algorithm used to produce the encryption key hash. Currently, the only accepted value is "AES256". Must be provided if the x-ms-encryption-key header is provided. /// If file should be flushed after the append. + /// Required if the request body is a structured message. Specifies the message schema version and properties. + /// Required if the request body is a structured message. Specifies the length of the blob/file content inside the message body. Will always be smaller than Content-Length. /// The cancellation token to use. /// is null. - public ResponseWithHeaders AppendData(Stream body, long? position = null, int? timeout = null, long? contentLength = null, byte[] transactionalContentHash = null, byte[] transactionalContentCrc64 = null, string leaseId = null, DataLakeLeaseAction? leaseAction = null, long? leaseDuration = null, string proposedLeaseId = null, string encryptionKey = null, string encryptionKeySha256 = null, EncryptionAlgorithmTypeInternal? encryptionAlgorithm = null, bool? flush = null, CancellationToken cancellationToken = default) + public ResponseWithHeaders AppendData(Stream body, long? position = null, int? timeout = null, long? contentLength = null, byte[] transactionalContentHash = null, byte[] transactionalContentCrc64 = null, string leaseId = null, DataLakeLeaseAction? leaseAction = null, long? leaseDuration = null, string proposedLeaseId = null, string encryptionKey = null, string encryptionKeySha256 = null, EncryptionAlgorithmTypeInternal? encryptionAlgorithm = null, bool? flush = null, string structuredBodyType = null, long? structuredContentLength = null, CancellationToken cancellationToken = default) { if (body == null) { throw new ArgumentNullException(nameof(body)); } - using var message = CreateAppendDataRequest(body, position, timeout, contentLength, transactionalContentHash, transactionalContentCrc64, leaseId, leaseAction, leaseDuration, proposedLeaseId, encryptionKey, encryptionKeySha256, encryptionAlgorithm, flush); + using var message = CreateAppendDataRequest(body, position, timeout, contentLength, transactionalContentHash, transactionalContentCrc64, leaseId, leaseAction, leaseDuration, proposedLeaseId, encryptionKey, encryptionKeySha256, encryptionAlgorithm, flush, structuredBodyType, structuredContentLength); _pipeline.Send(message, cancellationToken); var headers = new PathAppendDataHeaders(message.Response); switch (message.Response.Status) diff --git a/sdk/storage/Azure.Storage.Files.DataLake/src/Generated/PathUpdateHeaders.cs b/sdk/storage/Azure.Storage.Files.DataLake/src/Generated/PathUpdateHeaders.cs index 35668cb1c3a1d..026c78e72481a 100644 --- a/sdk/storage/Azure.Storage.Files.DataLake/src/Generated/PathUpdateHeaders.cs +++ b/sdk/storage/Azure.Storage.Files.DataLake/src/Generated/PathUpdateHeaders.cs @@ -43,5 +43,7 @@ public PathUpdateHeaders(Response response) public string XMsContinuation => _response.Headers.TryGetValue("x-ms-continuation", out string value) ? value : null; /// The version of the REST protocol used to process the request. public string Version => _response.Headers.TryGetValue("x-ms-version", out string value) ? value : null; + /// Indicates the structured message body was accepted and mirrors back the message schema version and properties. + public string StructuredBodyType => _response.Headers.TryGetValue("x-ms-structured-body", out string value) ? value : null; } } diff --git a/sdk/storage/Azure.Storage.Files.DataLake/src/Generated/ServiceRestClient.cs b/sdk/storage/Azure.Storage.Files.DataLake/src/Generated/ServiceRestClient.cs index 118595b4d87d1..b00fa12238f4e 100644 --- a/sdk/storage/Azure.Storage.Files.DataLake/src/Generated/ServiceRestClient.cs +++ b/sdk/storage/Azure.Storage.Files.DataLake/src/Generated/ServiceRestClient.cs @@ -28,7 +28,7 @@ internal partial class ServiceRestClient /// The handler for diagnostic messaging in the client. /// The HTTP pipeline for sending and receiving REST requests and responses. /// The URL of the service account, container, or blob that is the target of the desired operation. - /// Specifies the version of the operation to use for this request. The default value is "2023-05-03". + /// Specifies the version of the operation to use for this request. The default value is "2025-01-05". /// , , or is null. public ServiceRestClient(ClientDiagnostics clientDiagnostics, HttpPipeline pipeline, string url, string version) { diff --git a/sdk/storage/Azure.Storage.Files.DataLake/src/Models/DataLakeFileReadResult.cs b/sdk/storage/Azure.Storage.Files.DataLake/src/Models/DataLakeFileReadResult.cs new file mode 100644 index 0000000000000..6059019eb6cdd --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/src/Models/DataLakeFileReadResult.cs @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; + +namespace Azure.Storage.Files.DataLake.Models +{ + /// + /// The details and content returned from reading a DataLake File. + /// + public class DataLakeFileReadResult + { + internal DataLakeFileReadResult() { } + + /// + /// Details returned when reading a DataLake file + /// + public FileDownloadDetails Details { get; internal set; } + + /// + /// Content. + /// + public BinaryData Content { get; internal set; } + } +} diff --git a/sdk/storage/Azure.Storage.Files.DataLake/src/Models/DataLakeFileReadStreamingResult.cs b/sdk/storage/Azure.Storage.Files.DataLake/src/Models/DataLakeFileReadStreamingResult.cs new file mode 100644 index 0000000000000..985b1e9ab8374 --- /dev/null +++ b/sdk/storage/Azure.Storage.Files.DataLake/src/Models/DataLakeFileReadStreamingResult.cs @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.IO; +using Azure.Storage.Shared; + +namespace Azure.Storage.Files.DataLake.Models +{ + /// + /// The details and content returned from reading a datalake file. + /// + public class DataLakeFileReadStreamingResult : IDisposable + { + internal DataLakeFileReadStreamingResult() { } + + /// + /// Details returned when reading a datalake file. + /// + public FileDownloadDetails Details { get; internal set; } + + /// + /// Content. + /// + public Stream Content { get; internal set; } + + /// + /// Disposes the by calling Dispose on the underlying stream. + /// + public void Dispose() + { + Content?.Dispose(); + GC.SuppressFinalize(this); + } + } +} diff --git a/sdk/storage/Azure.Storage.Files.DataLake/src/Models/DataLakeModelFactory.cs b/sdk/storage/Azure.Storage.Files.DataLake/src/Models/DataLakeModelFactory.cs index f4b7370f69f4e..34e280c2b2f63 100644 --- a/sdk/storage/Azure.Storage.Files.DataLake/src/Models/DataLakeModelFactory.cs +++ b/sdk/storage/Azure.Storage.Files.DataLake/src/Models/DataLakeModelFactory.cs @@ -13,6 +13,34 @@ namespace Azure.Storage.Files.DataLake.Models /// public static partial class DataLakeModelFactory { + #region DataLakeFileReadResult + /// + /// Creates a new instance for mocking. + /// + public static DataLakeFileReadResult DataLakeFileReadResult( + BinaryData content, + FileDownloadDetails details) + => new DataLakeFileReadResult() + { + Content = content, + Details = details + }; + #endregion DataLakeFileReadResult + + #region DataLakeFileReadStreamingResult + /// + /// Creates a new instance for mocking. + /// + public static DataLakeFileReadStreamingResult DataLakeFileReadStreamingResult( + Stream content, + FileDownloadDetails details) + => new DataLakeFileReadStreamingResult() + { + Content = content, + Details = details + }; + #endregion DataLakeFileReadStreamingResult + #region FileDownloadDetails /// /// Creates a new FileDownloadDetails instance for mocking. @@ -235,6 +263,7 @@ public static FileDownloadDetails FileDownloadDetails( /// /// Creates a new instance for mocking. /// + [EditorBrowsable(EditorBrowsableState.Never)] public static FileDownloadInfo FileDownloadInfo( long contentLength, Stream content, diff --git a/sdk/storage/Azure.Storage.Files.DataLake/src/autorest.md b/sdk/storage/Azure.Storage.Files.DataLake/src/autorest.md index 4121ebab9932e..ec9675a014f70 100644 --- a/sdk/storage/Azure.Storage.Files.DataLake/src/autorest.md +++ b/sdk/storage/Azure.Storage.Files.DataLake/src/autorest.md @@ -4,7 +4,7 @@ Run `dotnet build /t:GenerateCode` to generate code. ``` yaml input-file: - - https://raw.githubusercontent.com/Azure/azure-rest-api-specs/5da3c08b92d05858b728b013b69502dc93485373/specification/storage/data-plane/Azure.Storage.Files.DataLake/stable/2023-05-03/DataLakeStorage.json + - https://raw.githubusercontent.com/Azure/azure-rest-api-specs/a936baeb45003f1d31ce855084b2e54365af78af/specification/storage/data-plane/Azure.Storage.Files.DataLake/stable/2025-01-05/DataLakeStorage.json generation1-convenience-client: true modelerfour: seal-single-value-enum-by-default: true diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/DataLakeClientTestFixtureAttribute.cs b/sdk/storage/Azure.Storage.Files.DataLake/tests/DataLakeClientTestFixtureAttribute.cs index 1a3cee805c368..eab0498c5dfcc 100644 --- a/sdk/storage/Azure.Storage.Files.DataLake/tests/DataLakeClientTestFixtureAttribute.cs +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/DataLakeClientTestFixtureAttribute.cs @@ -33,6 +33,7 @@ public DataLakeClientTestFixtureAttribute() DataLakeClientOptions.ServiceVersion.V2024_05_04, DataLakeClientOptions.ServiceVersion.V2024_08_04, DataLakeClientOptions.ServiceVersion.V2024_11_04, + DataLakeClientOptions.ServiceVersion.V2025_01_05, StorageVersionExtensions.LatestVersion, StorageVersionExtensions.MaxVersion) { diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/DirectoryClientTests.cs b/sdk/storage/Azure.Storage.Files.DataLake/tests/DirectoryClientTests.cs index 44f5b378e26df..83938361fb0d0 100644 --- a/sdk/storage/Azure.Storage.Files.DataLake/tests/DirectoryClientTests.cs +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/DirectoryClientTests.cs @@ -6470,7 +6470,424 @@ public void GenerateSas_BuilderIsDirectoryError() // Act TestHelper.AssertExpectedException( () => directoryClient.GenerateSasUri(sasBuilder), - new InvalidOperationException("SAS Uri cannot be generated. Expected builder.IsDirectory to be set to true to generatethe respective SAS for the client, GetType")); + new InvalidOperationException("SAS Uri cannot be generated. Expected builder.IsDirectory to be set to true to generate the respective SAS for the client, GetType")); + } + #endregion + + #region GenerateUserDelegationSasTests + [RecordedTest] + public async Task GenerateUserDelegationSas_RequiredParameters() + { + // Arrange + TestConstants constants = TestConstants.Create(this); + string fileSystemName = GetNewFileSystemName(); + string path = GetNewDirectoryName(); + Uri serviceUri = new Uri($"https://{constants.Sas.Account}.dfs.core.windows.net"); + DataLakeUriBuilder dataLakeUriBuilder = new DataLakeUriBuilder(serviceUri) + { + FileSystemName = fileSystemName, + DirectoryOrFilePath = path + }; + DataLakeSasPermissions permissions = DataLakeSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + DataLakeDirectoryClient directoryClient = InstrumentClient(new DataLakeDirectoryClient( + dataLakeUriBuilder.ToUri(), + GetOptions())); + + string stringToSign = null; + Response userDelegationKeyResponse = await GetServiceClient_OAuth().GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: Recording.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + + // Act + Uri sasUri = directoryClient.GenerateUserDelegationSasUri(permissions, expiresOn, userDelegationKey, out stringToSign); + + // Assert + DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName, + Path = path, + IsDirectory = true + }; + DataLakeUriBuilder expectedUri = new DataLakeUriBuilder(serviceUri) + { + FileSystemName = fileSystemName, + DirectoryOrFilePath = path, + Sas = sasBuilder.ToSasQueryParameters(userDelegationKey, directoryClient.AccountName) + }; + + Assert.AreEqual(expectedUri.ToUri(), sasUri); + Assert.IsNotNull(stringToSign); + } + + [RecordedTest] + public async Task GenerateUserDelegationSas_Builder() + { + TestConstants constants = TestConstants.Create(this); + string fileSystemName = GetNewFileSystemName(); + string path = GetNewDirectoryName(); + Uri serviceUri = new Uri($"https://{constants.Sas.Account}.dfs.core.windows.net"); + DataLakeUriBuilder dataLakeUriBuilder = new DataLakeUriBuilder(serviceUri) + { + FileSystemName = fileSystemName, + DirectoryOrFilePath = path + }; + DataLakeSasPermissions permissions = DataLakeSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + DataLakeDirectoryClient directoryClient = InstrumentClient(new DataLakeDirectoryClient( + dataLakeUriBuilder.ToUri(), + GetOptions())); + + DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName, + Path = path, + IsDirectory = true, + }; + + string stringToSign = null; + Response userDelegationKeyResponse = await GetServiceClient_OAuth().GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: Recording.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + + // Act + Uri sasUri = directoryClient.GenerateUserDelegationSasUri(sasBuilder, userDelegationKey, out stringToSign); + + // Assert + DataLakeUriBuilder expectedUri = new DataLakeUriBuilder(serviceUri) + { + FileSystemName = fileSystemName, + DirectoryOrFilePath = path + }; + DataLakeSasBuilder sasBuilder2 = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName, + Path = path, + IsDirectory = true, + }; + expectedUri.Sas = sasBuilder2.ToSasQueryParameters(userDelegationKey, directoryClient.AccountName); + Assert.AreEqual(expectedUri.ToUri(), sasUri); + Assert.IsNotNull(stringToSign); + } + + [RecordedTest] + public async Task GenerateUserDelegationSas_BuilderNull() + { + TestConstants constants = TestConstants.Create(this); + string fileSystemName = GetNewFileSystemName(); + string path = GetNewDirectoryName(); + Uri serviceUri = new Uri($"https://{constants.Sas.Account}.dfs.core.windows.net"); + DataLakeUriBuilder dataLakeUriBuilder = new DataLakeUriBuilder(serviceUri) + { + FileSystemName = fileSystemName, + DirectoryOrFilePath = path + }; + DataLakeDirectoryClient directoryClient = InstrumentClient(new DataLakeDirectoryClient( + dataLakeUriBuilder.ToUri(), + GetOptions())); + + string stringToSign = null; + Response userDelegationKeyResponse = await GetServiceClient_OAuth().GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: Recording.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + + // Act + TestHelper.AssertExpectedException( + () => directoryClient.GenerateUserDelegationSasUri(null, userDelegationKey, out stringToSign), + new ArgumentNullException("builder")); + } + + [RecordedTest] + public void GenerateUserDelegationSas_UserDelegationKeyNull() + { + TestConstants constants = TestConstants.Create(this); + string fileSystemName = GetNewFileSystemName(); + string path = GetNewDirectoryName(); + Uri serviceUri = new Uri($"https://{constants.Sas.Account}.dfs.core.windows.net"); + DataLakeUriBuilder dataLakeUriBuilder = new DataLakeUriBuilder(serviceUri) + { + FileSystemName = fileSystemName, + DirectoryOrFilePath = path + }; + DataLakeSasPermissions permissions = DataLakeSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + DataLakeDirectoryClient directoryClient = InstrumentClient(new DataLakeDirectoryClient( + dataLakeUriBuilder.ToUri(), + GetOptions())); + + DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName, + Path = path, + IsDirectory = true, + }; + + string stringToSign = null; + + // Assert + TestHelper.AssertExpectedException( + () => directoryClient.GenerateUserDelegationSasUri(sasBuilder, null, out stringToSign), + new ArgumentNullException("userDelegationKey")); + } + + [RecordedTest] + public async Task GenerateUserDelegationSas_BuilderNullFileSystemName() + { + TestConstants constants = TestConstants.Create(this); + string fileSystemName = GetNewFileSystemName(); + string path = GetNewDirectoryName(); + Uri serviceUri = new Uri($"https://{constants.Sas.Account}.dfs.core.windows.net"); + DataLakeUriBuilder dataLakeUriBuilder = new DataLakeUriBuilder(serviceUri) + { + FileSystemName = fileSystemName, + DirectoryOrFilePath = path + }; + DataLakeSasPermissions permissions = DataLakeSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + DataLakeDirectoryClient directoryClient = InstrumentClient(new DataLakeDirectoryClient( + dataLakeUriBuilder.ToUri(), + GetOptions())); + + DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = null, + Path = path, + IsDirectory = true, + }; + + Response userDelegationKeyResponse = await GetServiceClient_OAuth().GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: Recording.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + + // Act + Uri sasUri = directoryClient.GenerateUserDelegationSasUri(sasBuilder, userDelegationKey); + + // Assert + DataLakeUriBuilder expectedUri = new DataLakeUriBuilder(serviceUri) + { + FileSystemName = fileSystemName, + DirectoryOrFilePath = path + }; + DataLakeSasBuilder sasBuilder2 = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName, + Path = path, + IsDirectory = true, + }; + expectedUri.Sas = sasBuilder2.ToSasQueryParameters(userDelegationKey, directoryClient.AccountName); + Assert.AreEqual(expectedUri.ToUri(), sasUri); + } + + [RecordedTest] + public async Task GenerateUserDelegationSas_BuilderWrongFileSystemName() + { + // Arrange + TestConstants constants = TestConstants.Create(this); + string fileSystemName = GetNewFileSystemName(); + string directoryName = GetNewDirectoryName(); + Uri serviceUri = new Uri($"https://{constants.Sas.Account}.dfs.core.windows.net"); + DataLakeUriBuilder dataLakeUriBuilder = new DataLakeUriBuilder(serviceUri) + { + FileSystemName = fileSystemName, + DirectoryOrFilePath = directoryName + }; + DataLakeSasPermissions permissions = DataLakeSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + DataLakeDirectoryClient directoryClient = InstrumentClient(new DataLakeDirectoryClient( + dataLakeUriBuilder.ToUri(), + GetOptions())); + + DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = GetNewFileSystemName(), // different filesytem name + Path = directoryName, + IsDirectory = true + }; + + Response userDelegationKeyResponse = await GetServiceClient_OAuth().GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: Recording.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + + // Act + TestHelper.AssertExpectedException( + () => directoryClient.GenerateUserDelegationSasUri(sasBuilder, userDelegationKey), + new InvalidOperationException("SAS Uri cannot be generated. DataLakeSasBuilder.FileSystemName does not match FileSystemName in the Client. DataLakeSasBuilder.FileSystemName must either be left empty or match the FileSystemName in the Client")); + } + + [RecordedTest] + public async Task GenerateUserDelegationSas_BuilderNullPath() + { + TestConstants constants = TestConstants.Create(this); + string fileSystemName = GetNewFileSystemName(); + string path = GetNewDirectoryName(); + Uri serviceUri = new Uri($"https://{constants.Sas.Account}.dfs.core.windows.net"); + DataLakeUriBuilder dataLakeUriBuilder = new DataLakeUriBuilder(serviceUri) + { + FileSystemName = fileSystemName, + DirectoryOrFilePath = path + }; + DataLakeSasPermissions permissions = DataLakeSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + DataLakeDirectoryClient directoryClient = InstrumentClient(new DataLakeDirectoryClient( + dataLakeUriBuilder.ToUri(), + GetOptions())); + + DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName, + Path = null, + IsDirectory = true, + }; + + Response userDelegationKeyResponse = await GetServiceClient_OAuth().GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: Recording.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + + // Act + Uri sasUri = directoryClient.GenerateUserDelegationSasUri(sasBuilder, userDelegationKey); + + // Assert + DataLakeUriBuilder expectedUri = new DataLakeUriBuilder(serviceUri) + { + FileSystemName = fileSystemName, + DirectoryOrFilePath = path + }; + DataLakeSasBuilder sasBuilder2 = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName, + Path = path, + IsDirectory = true, + }; + expectedUri.Sas = sasBuilder2.ToSasQueryParameters(userDelegationKey, directoryClient.AccountName); + Assert.AreEqual(expectedUri.ToUri(), sasUri); + } + + [RecordedTest] + public async Task GenerateUserDelegationSas_BuilderWrongPathName() + { + // Arrange + TestConstants constants = TestConstants.Create(this); + string directoryName = GetNewDirectoryName(); + string fileSystemName = GetNewFileSystemName(); + Uri serviceUri = new Uri($"https://{constants.Sas.Account}.dfs.core.windows.net"); + DataLakeUriBuilder dataLakeUriBuilder = new DataLakeUriBuilder(serviceUri) + { + FileSystemName = fileSystemName, + DirectoryOrFilePath = directoryName + }; + DataLakeSasPermissions permissions = DataLakeSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + DataLakeDirectoryClient directoryClient = InstrumentClient(new DataLakeDirectoryClient( + dataLakeUriBuilder.ToUri(), + GetOptions())); + + DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName, + Path = GetNewDirectoryName(), // different directory name + IsDirectory = true, + }; + + Response userDelegationKeyResponse = await GetServiceClient_OAuth().GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: Recording.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + + // Act + TestHelper.AssertExpectedException( + () => directoryClient.GenerateUserDelegationSasUri(sasBuilder, userDelegationKey), + new InvalidOperationException("SAS Uri cannot be generated. DataLakeSasBuilder.Path does not match Path in the Client. DataLakeSasBuilder.Path must either be left empty or match the Path in the Client")); + } + + [RecordedTest] + public async Task GenerateUserDelegationSas_BuilderIsDirectoryNull() + { + TestConstants constants = TestConstants.Create(this); + string fileSystemName = GetNewFileSystemName(); + string path = GetNewDirectoryName(); + Uri serviceUri = new Uri($"https://{constants.Sas.Account}.dfs.core.windows.net"); + DataLakeUriBuilder dataLakeUriBuilder = new DataLakeUriBuilder(serviceUri) + { + FileSystemName = fileSystemName, + DirectoryOrFilePath = path + }; + DataLakeSasPermissions permissions = DataLakeSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + DataLakeDirectoryClient directoryClient = InstrumentClient(new DataLakeDirectoryClient( + dataLakeUriBuilder.ToUri(), + GetOptions())); + + DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName, + Path = path, + IsDirectory = null, + }; + + Response userDelegationKeyResponse = await GetServiceClient_OAuth().GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: Recording.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + + // Act + Uri sasUri = directoryClient.GenerateUserDelegationSasUri(sasBuilder, userDelegationKey); + + // Assert + DataLakeUriBuilder expectedUri = new DataLakeUriBuilder(serviceUri) + { + FileSystemName = fileSystemName, + DirectoryOrFilePath = path + }; + DataLakeSasBuilder sasBuilder2 = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName, + Path = path, + IsDirectory = true, + }; + expectedUri.Sas = sasBuilder2.ToSasQueryParameters(userDelegationKey, directoryClient.AccountName); + Assert.AreEqual(expectedUri.ToUri(), sasUri); + } + + [RecordedTest] + public async Task GenerateUserDelegationSas_BuilderIsDirectoryError() + { + TestConstants constants = TestConstants.Create(this); + string fileSystemName = GetNewFileSystemName(); + string directoryName = GetNewDirectoryName(); + Uri serviceUri = new Uri($"https://{constants.Sas.Account}.dfs.core.windows.net"); + DataLakeUriBuilder dataLakeUriBuilder = new DataLakeUriBuilder(serviceUri) + { + FileSystemName = fileSystemName, + DirectoryOrFilePath = directoryName + }; + DataLakeSasPermissions permissions = DataLakeSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + DataLakeDirectoryClient directoryClient = InstrumentClient(new DataLakeDirectoryClient( + dataLakeUriBuilder.ToUri(), + GetOptions())); + + DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName, + Path = directoryName, + IsDirectory = false, + }; + + Response userDelegationKeyResponse = await GetServiceClient_OAuth().GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: Recording.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + + // Act + TestHelper.AssertExpectedException( + () => directoryClient.GenerateUserDelegationSasUri(sasBuilder, userDelegationKey), + new InvalidOperationException("SAS Uri cannot be generated. Expected builder.IsDirectory to be set to true to generate the respective SAS for the client, GetType")); } #endregion diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/FileClientTests.cs b/sdk/storage/Azure.Storage.Files.DataLake/tests/FileClientTests.cs index 6d9f0d5750800..ef9f73e40e18d 100644 --- a/sdk/storage/Azure.Storage.Files.DataLake/tests/FileClientTests.cs +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/FileClientTests.cs @@ -3455,6 +3455,71 @@ public async Task ReadAsync() TestHelper.AssertSequenceEqual(data, actual.ToArray()); } + [RecordedTest] + public async Task ReadStreamingAsync() + { + await using DisposingFileSystem test = await GetNewFileSystem(); + + // Arrange + byte[] data = GetRandomBuffer(Constants.KB); + DataLakeFileClient fileClient = await test.FileSystem.CreateFileAsync(GetNewFileName()); + using (MemoryStream stream = new MemoryStream(data)) + { + await fileClient.AppendAsync(stream, 0); + } + + await fileClient.FlushAsync(Constants.KB); + + // Act + Response response = await fileClient.ReadStreamingAsync(); + + // Assert + Assert.IsNotNull(response.Value.Details.LastModified); + Assert.IsNotNull(response.Value.Details.AcceptRanges); + Assert.IsNotNull(response.Value.Details.ETag); + Assert.IsNotNull(response.Value.Details.LeaseStatus); + Assert.IsNotNull(response.Value.Details.LeaseState); + Assert.IsNotNull(response.Value.Details.IsServerEncrypted); + Assert.IsNotNull(response.Value.Details.CreatedOn); + Assert.IsNotNull(response.Value.Details.Metadata); + + MemoryStream actual = new MemoryStream(); + await response.Value.Content.CopyToAsync(actual); + TestHelper.AssertSequenceEqual(data, actual.ToArray()); + } + + [RecordedTest] + public async Task ReadContentAsync() + { + await using DisposingFileSystem test = await GetNewFileSystem(); + + // Arrange + byte[] data = GetRandomBuffer(Constants.KB); + DataLakeFileClient fileClient = await test.FileSystem.CreateFileAsync(GetNewFileName()); + using (MemoryStream stream = new MemoryStream(data)) + { + await fileClient.AppendAsync(stream, 0); + } + + await fileClient.FlushAsync(Constants.KB); + + // Act + Response response = await fileClient.ReadContentAsync(); + + // Assert + Assert.IsNotNull(response.Value.Details.LastModified); + Assert.IsNotNull(response.Value.Details.AcceptRanges); + Assert.IsNotNull(response.Value.Details.ETag); + Assert.IsNotNull(response.Value.Details.LeaseStatus); + Assert.IsNotNull(response.Value.Details.LeaseState); + Assert.IsNotNull(response.Value.Details.IsServerEncrypted); + Assert.IsNotNull(response.Value.Details.CreatedOn); + Assert.IsNotNull(response.Value.Details.Metadata); + + byte[] actual = response.Value.Content.ToArray(); + TestHelper.AssertSequenceEqual(data, actual); + } + [RecordedTest] [ServiceVersion(Min = DataLakeClientOptions.ServiceVersion.V2024_05_04)] public async Task ReadAsyncACL() @@ -3502,6 +3567,97 @@ public async Task ReadAsyncACL() TestHelper.AssertSequenceEqual(data, actual.ToArray()); } + [RecordedTest] + [ServiceVersion(Min = DataLakeClientOptions.ServiceVersion.V2024_05_04)] + public async Task ReadStreamingAsyncACL() + { + await using DisposingFileSystem test = await GetNewFileSystem(publicAccessType: PublicAccessType.None); + DataLakeDirectoryClient directory = await test.FileSystem.CreateDirectoryAsync(GetNewDirectoryName()); + + DataLakeFileClient fileClient = InstrumentClient(directory.GetFileClient(GetNewFileName())); + + DataLakePathCreateOptions options = new DataLakePathCreateOptions + { + AccessOptions = new DataLakeAccessOptions + { + AccessControlList = AccessControlList + } + }; + + await fileClient.CreateAsync(options: options); + + // Arrange + var data = GetRandomBuffer(Constants.KB); + using (var stream = new MemoryStream(data)) + { + await fileClient.AppendAsync(stream, 0); + } + + await fileClient.FlushAsync(Constants.KB); + + // Act + Response response = await fileClient.ReadStreamingAsync(); + + // Assert + Assert.IsNotNull(response.Value.Details.LastModified); + Assert.IsNotNull(response.Value.Details.AcceptRanges); + Assert.IsNotNull(response.Value.Details.ETag); + Assert.IsNotNull(response.Value.Details.LeaseStatus); + Assert.IsNotNull(response.Value.Details.LeaseState); + Assert.IsNotNull(response.Value.Details.IsServerEncrypted); + Assert.IsNotNull(response.Value.Details.CreatedOn); + AssertAccessControlListEquality(AccessControlList, response.Value.Details.AccessControlList.ToList()); + + var actual = new MemoryStream(); + await response.Value.Content.CopyToAsync(actual); + TestHelper.AssertSequenceEqual(data, actual.ToArray()); + } + + [RecordedTest] + [ServiceVersion(Min = DataLakeClientOptions.ServiceVersion.V2024_05_04)] + public async Task ReadContentAsyncACL() + { + await using DisposingFileSystem test = await GetNewFileSystem(publicAccessType: PublicAccessType.None); + DataLakeDirectoryClient directory = await test.FileSystem.CreateDirectoryAsync(GetNewDirectoryName()); + + DataLakeFileClient fileClient = InstrumentClient(directory.GetFileClient(GetNewFileName())); + + DataLakePathCreateOptions options = new DataLakePathCreateOptions + { + AccessOptions = new DataLakeAccessOptions + { + AccessControlList = AccessControlList + } + }; + + await fileClient.CreateAsync(options: options); + + // Arrange + var data = GetRandomBuffer(Constants.KB); + using (var stream = new MemoryStream(data)) + { + await fileClient.AppendAsync(stream, 0); + } + + await fileClient.FlushAsync(Constants.KB); + + // Act + Response response = await fileClient.ReadContentAsync(); + + // Assert + Assert.IsNotNull(response.Value.Details.LastModified); + Assert.IsNotNull(response.Value.Details.AcceptRanges); + Assert.IsNotNull(response.Value.Details.ETag); + Assert.IsNotNull(response.Value.Details.LeaseStatus); + Assert.IsNotNull(response.Value.Details.LeaseState); + Assert.IsNotNull(response.Value.Details.IsServerEncrypted); + Assert.IsNotNull(response.Value.Details.CreatedOn); + AssertAccessControlListEquality(AccessControlList, response.Value.Details.AccessControlList.ToList()); + + byte[] actual = response.Value.Content.ToArray(); + TestHelper.AssertSequenceEqual(data, actual); + } + [RecordedTest] [ServiceVersion(Min = DataLakeClientOptions.ServiceVersion.V2024_05_04)] public async Task GetPropertiesAsyncACL() @@ -3630,6 +3786,76 @@ public async Task ReadAsync_Conditions() } } + [RecordedTest] + public async Task ReadStreamingAsync_Conditions() + { + var garbageLeaseId = GetGarbageLeaseId(); + foreach (AccessConditionParameters parameters in Conditions_Data) + { + await using DisposingFileSystem test = await GetNewFileSystem(); + + // Arrange + var data = GetRandomBuffer(Constants.KB); + DataLakeFileClient file = await test.FileSystem.CreateFileAsync(GetNewFileName()); + using (var stream = new MemoryStream(data)) + { + await file.AppendAsync(stream, 0); + } + + await file.FlushAsync(Constants.KB); + + parameters.Match = await SetupPathMatchCondition(file, parameters.Match); + parameters.LeaseId = await SetupPathLeaseCondition(file, parameters.LeaseId, garbageLeaseId); + DataLakeRequestConditions conditions = BuildDataLakeRequestConditions( + parameters: parameters, + lease: true); + + // Act + Response response = await file.ReadStreamingAsync(new DataLakeFileReadOptions + { + Conditions = conditions + }); + + // Assert + Assert.IsNotNull(response.GetRawResponse().Headers.RequestId); + } + } + + [RecordedTest] + public async Task ReadContentAsync_Conditions() + { + var garbageLeaseId = GetGarbageLeaseId(); + foreach (AccessConditionParameters parameters in Conditions_Data) + { + await using DisposingFileSystem test = await GetNewFileSystem(); + + // Arrange + var data = GetRandomBuffer(Constants.KB); + DataLakeFileClient file = await test.FileSystem.CreateFileAsync(GetNewFileName()); + using (var stream = new MemoryStream(data)) + { + await file.AppendAsync(stream, 0); + } + + await file.FlushAsync(Constants.KB); + + parameters.Match = await SetupPathMatchCondition(file, parameters.Match); + parameters.LeaseId = await SetupPathLeaseCondition(file, parameters.LeaseId, garbageLeaseId); + DataLakeRequestConditions conditions = BuildDataLakeRequestConditions( + parameters: parameters, + lease: true); + + // Act + Response response = await file.ReadContentAsync(new DataLakeFileReadOptions + { + Conditions = conditions + }); + + // Assert + Assert.IsNotNull(response.GetRawResponse().Headers.RequestId); + } + } + [RecordedTest] public async Task ReadAsync_ConditionsFail() { @@ -3663,6 +3889,72 @@ await TestHelper.CatchAsync( } } + [RecordedTest] + public async Task ReadStreamingAsync_ConditionsFail() + { + var garbageLeaseId = GetGarbageLeaseId(); + foreach (AccessConditionParameters parameters in GetConditionsFail_Data(garbageLeaseId)) + { + await using DisposingFileSystem test = await GetNewFileSystem(); + + // Arrange + var data = GetRandomBuffer(Constants.KB); + DataLakeFileClient file = await test.FileSystem.CreateFileAsync(GetNewFileName()); + using (var stream = new MemoryStream(data)) + { + await file.AppendAsync(stream, 0); + } + + await file.FlushAsync(Constants.KB); + + parameters.NoneMatch = await SetupPathMatchCondition(file, parameters.NoneMatch); + DataLakeRequestConditions conditions = BuildDataLakeRequestConditions(parameters); + + // Act + await TestHelper.CatchAsync( + async () => + { + var _ = (await file.ReadStreamingAsync(new DataLakeFileReadOptions + { + Conditions = conditions + })).Value; + }); + } + } + + [RecordedTest] + public async Task ReadContentAsync_ConditionsFail() + { + var garbageLeaseId = GetGarbageLeaseId(); + foreach (AccessConditionParameters parameters in GetConditionsFail_Data(garbageLeaseId)) + { + await using DisposingFileSystem test = await GetNewFileSystem(); + + // Arrange + var data = GetRandomBuffer(Constants.KB); + DataLakeFileClient file = await test.FileSystem.CreateFileAsync(GetNewFileName()); + using (var stream = new MemoryStream(data)) + { + await file.AppendAsync(stream, 0); + } + + await file.FlushAsync(Constants.KB); + + parameters.NoneMatch = await SetupPathMatchCondition(file, parameters.NoneMatch); + DataLakeRequestConditions conditions = BuildDataLakeRequestConditions(parameters); + + // Act + await TestHelper.CatchAsync( + async () => + { + var _ = (await file.ReadContentAsync(new DataLakeFileReadOptions + { + Conditions = conditions + })).Value; + }); + } + } + [RecordedTest] public async Task ReadAsync_Error() { @@ -3677,6 +3969,34 @@ await TestHelper.AssertExpectedExceptionAsync( e => Assert.AreEqual("BlobNotFound", e.ErrorCode)); } + [RecordedTest] + public async Task ReadStreamingAsync_Error() + { + await using DisposingFileSystem test = await GetNewFileSystem(); + + // Arrange + DataLakeFileClient file = InstrumentClient(test.FileSystem.GetFileClient(GetNewFileName())); + + // Act + await TestHelper.AssertExpectedExceptionAsync( + file.ReadStreamingAsync(), + e => Assert.AreEqual("BlobNotFound", e.ErrorCode)); + } + + [RecordedTest] + public async Task ReadContentAsync_Error() + { + await using DisposingFileSystem test = await GetNewFileSystem(); + + // Arrange + DataLakeFileClient file = InstrumentClient(test.FileSystem.GetFileClient(GetNewFileName())); + + // Act + await TestHelper.AssertExpectedExceptionAsync( + file.ReadContentAsync(), + e => Assert.AreEqual("BlobNotFound", e.ErrorCode)); + } + [RecordedTest] public async Task AcquireLeaseAsync() { @@ -5712,69 +6032,337 @@ public async Task OpenWriteAsync_Close() byte[] data = GetRandomBuffer(Constants.KB); using Stream stream = new MemoryStream(data); - DataLakeFileOpenWriteOptions options = new DataLakeFileOpenWriteOptions + DataLakeFileOpenWriteOptions options = new DataLakeFileOpenWriteOptions + { + Close = true, + BufferSize = 256 + }; + + // Act + Stream openWriteStream = await file.OpenWriteAsync( + overwrite: false, + options); + await stream.CopyToAsync(openWriteStream); + await openWriteStream.FlushAsync(); + } + + #region GenerateSasTests + [RecordedTest] + public void CanGenerateSas_ClientConstructors() + { + // Arrange + var constants = TestConstants.Create(this); + var blobEndpoint = new Uri("https://127.0.0.1/" + constants.Sas.Account); + + // Act - DataLakeFileClient(Uri blobContainerUri, fileClientOptions options = default) + DataLakeFileClient file = InstrumentClient(new DataLakeFileClient( + blobEndpoint, + GetOptions())); + Assert.IsFalse(file.CanGenerateSasUri); + + // Act - DataLakeFileClient(Uri blobContainerUri, StorageSharedKeyCredential credential, fileClientOptions options = default) + DataLakeFileClient file2 = InstrumentClient(new DataLakeFileClient( + blobEndpoint, + constants.Sas.SharedKeyCredential, + GetOptions())); + Assert.IsTrue(file2.CanGenerateSasUri); + + // Act - DataLakeFileClient(Uri blobContainerUri, TokenCredential credential, fileClientOptions options = default) + var tokenCredentials = new DefaultAzureCredential(); + DataLakeFileClient file3 = InstrumentClient(new DataLakeFileClient( + blobEndpoint, + tokenCredentials, + GetOptions())); + Assert.IsFalse(file3.CanGenerateSasUri); + } + + [RecordedTest] + public void CanGenerateSas_Mockable() + { + // Act + var file = new Mock(); + file.Setup(x => x.CanGenerateSasUri).Returns(false); + + // Assert + Assert.IsFalse(file.Object.CanGenerateSasUri); + + // Act + file.Setup(x => x.CanGenerateSasUri).Returns(true); + + // Assert + Assert.IsTrue(file.Object.CanGenerateSasUri); + } + + [RecordedTest] + public void GenerateSas_RequiredParameters() + { + // Arrange + TestConstants constants = TestConstants.Create(this); + string fileSystemName = GetNewFileSystemName(); + string path = GetNewFileName(); + DataLakeSasPermissions permissions = DataLakeSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + Uri serviceUri = new Uri($"https://{constants.Sas.Account}.dfs.core.windows.net"); + DataLakeUriBuilder dataLakeUriBuilder = new DataLakeUriBuilder(serviceUri) + { + FileSystemName = fileSystemName, + DirectoryOrFilePath = path + }; + DataLakeFileClient fileClient = InstrumentClient(new DataLakeFileClient( + dataLakeUriBuilder.ToUri(), + constants.Sas.SharedKeyCredential, + GetOptions())); + + // Act + Uri sasUri = fileClient.GenerateSasUri(permissions, expiresOn); + + // Assert + DataLakeSasBuilder sasBuilder2 = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName, + Path = path + }; + DataLakeUriBuilder expectedUri = new DataLakeUriBuilder(serviceUri) + { + FileSystemName = fileSystemName, + DirectoryOrFilePath = path, + Sas = sasBuilder2.ToSasQueryParameters(constants.Sas.SharedKeyCredential) + }; + Assert.AreEqual(expectedUri.ToUri(), sasUri); + } + + [RecordedTest] + public void GenerateSas_Builder() + { + TestConstants constants = TestConstants.Create(this); + string fileSystemName = GetNewFileSystemName(); + string path = GetNewFileName(); + DataLakeSasPermissions permissions = DataLakeSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + Uri serviceUri = new Uri($"https://{constants.Sas.Account}.dfs.core.windows.net"); + DataLakeUriBuilder dataLakeUriBuilder = new DataLakeUriBuilder(serviceUri) + { + FileSystemName = fileSystemName, + DirectoryOrFilePath = path + }; + DataLakeFileClient fileClient = InstrumentClient(new DataLakeFileClient( + dataLakeUriBuilder.ToUri(), + constants.Sas.SharedKeyCredential, + GetOptions())); + + DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName, + Path = path + }; + + // Act + Uri sasUri = fileClient.GenerateSasUri(sasBuilder); + + // Assert + DataLakeSasBuilder sasBuilder2 = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName, + Path = path, + }; + DataLakeUriBuilder expectedUri = new DataLakeUriBuilder(serviceUri) + { + FileSystemName = fileSystemName, + DirectoryOrFilePath = path, + Sas = sasBuilder2.ToSasQueryParameters(constants.Sas.SharedKeyCredential) + }; + Assert.AreEqual(expectedUri.ToUri(), sasUri); + } + + [RecordedTest] + public void GenerateSas_BuilderNullFileSystemName() + { + TestConstants constants = TestConstants.Create(this); + string fileSystemName = GetNewFileSystemName(); + string path = GetNewFileName(); + DataLakeSasPermissions permissions = DataLakeSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + Uri serviceUri = new Uri($"https://{constants.Sas.Account}.dfs.core.windows.net"); + DataLakeUriBuilder dataLakeUriBuilder = new DataLakeUriBuilder(serviceUri) + { + FileSystemName = fileSystemName, + DirectoryOrFilePath = path + }; + DataLakeFileClient fileClient = InstrumentClient(new DataLakeFileClient( + dataLakeUriBuilder.ToUri(), + constants.Sas.SharedKeyCredential, + GetOptions())); + + DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = null, + Path = path + }; + + // Act + Uri sasUri = fileClient.GenerateSasUri(sasBuilder); + + // Assert + DataLakeSasBuilder sasBuilder2 = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName, + Path = path, + }; + DataLakeUriBuilder expectedUri = new DataLakeUriBuilder(serviceUri) + { + FileSystemName = fileSystemName, + DirectoryOrFilePath = path, + Sas = sasBuilder2.ToSasQueryParameters(constants.Sas.SharedKeyCredential) + }; + Assert.AreEqual(expectedUri.ToUri(), sasUri); + } + + [RecordedTest] + public void GenerateSas_BuilderWrongFileSystemName() + { + // Arrange + TestConstants constants = TestConstants.Create(this); + string fileSystemName = GetNewFileSystemName(); + string path = GetNewFileName(); + Uri serviceUri = new Uri($"https://{constants.Sas.Account}.dfs.core.windows.net"); + DataLakeUriBuilder dataLakeUriBuilder = new DataLakeUriBuilder(serviceUri) + { + FileSystemName = fileSystemName, + DirectoryOrFilePath = path + }; + DataLakeSasPermissions permissions = DataLakeSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + DataLakeFileClient fileClient = InstrumentClient(new DataLakeFileClient( + dataLakeUriBuilder.ToUri(), + constants.Sas.SharedKeyCredential, + GetOptions())); + + DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = GetNewFileSystemName(), // different filesystem name + Path = path, + }; + + // Act + TestHelper.AssertExpectedException( + () => fileClient.GenerateSasUri(sasBuilder), + new InvalidOperationException("SAS Uri cannot be generated. DataLakeSasBuilder.FileSystemName does not match FileSystemName in the Client. DataLakeSasBuilder.FileSystemName must either be left empty or match the FileSystemName in the Client")); + } + + [RecordedTest] + public void GenerateSas_BuilderNullFileName() + { + TestConstants constants = TestConstants.Create(this); + string fileSystemName = GetNewFileSystemName(); + string path = GetNewFileName(); + DataLakeSasPermissions permissions = DataLakeSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + Uri serviceUri = new Uri($"https://{constants.Sas.Account}.dfs.core.windows.net"); + DataLakeUriBuilder dataLakeUriBuilder = new DataLakeUriBuilder(serviceUri) + { + FileSystemName = fileSystemName, + DirectoryOrFilePath = path + }; + DataLakeFileClient fileClient = InstrumentClient(new DataLakeFileClient( + dataLakeUriBuilder.ToUri(), + constants.Sas.SharedKeyCredential, + GetOptions())); + + DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder(permissions, expiresOn) { - Close = true, - BufferSize = 256 + FileSystemName = fileSystemName, + Path = null }; // Act - Stream openWriteStream = await file.OpenWriteAsync( - overwrite: false, - options); - await stream.CopyToAsync(openWriteStream); - await openWriteStream.FlushAsync(); + Uri sasUri = fileClient.GenerateSasUri(sasBuilder); + + // Assert + DataLakeSasBuilder sasBuilder2 = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName, + Path = path, + }; + DataLakeUriBuilder expectedUri = new DataLakeUriBuilder(serviceUri) + { + FileSystemName = fileSystemName, + DirectoryOrFilePath = path, + Sas = sasBuilder2.ToSasQueryParameters(constants.Sas.SharedKeyCredential) + }; + Assert.AreEqual(expectedUri.ToUri(), sasUri); } - #region GenerateSasTests [RecordedTest] - public void CanGenerateSas_ClientConstructors() + public void GenerateSas_BuilderWrongFileName() { // Arrange - var constants = TestConstants.Create(this); - var blobEndpoint = new Uri("https://127.0.0.1/" + constants.Sas.Account); - - // Act - DataLakeFileClient(Uri blobContainerUri, fileClientOptions options = default) - DataLakeFileClient file = InstrumentClient(new DataLakeFileClient( - blobEndpoint, - GetOptions())); - Assert.IsFalse(file.CanGenerateSasUri); - - // Act - DataLakeFileClient(Uri blobContainerUri, StorageSharedKeyCredential credential, fileClientOptions options = default) - DataLakeFileClient file2 = InstrumentClient(new DataLakeFileClient( - blobEndpoint, + TestConstants constants = TestConstants.Create(this); + string fileSystemName = GetNewFileSystemName(); + string path = GetNewFileName(); + Uri serviceUri = new Uri($"https://{constants.Sas.Account}.dfs.core.windows.net"); + DataLakeUriBuilder dataLakeUriBuilder = new DataLakeUriBuilder(serviceUri) + { + FileSystemName = fileSystemName, + DirectoryOrFilePath = path + }; + DataLakeSasPermissions permissions = DataLakeSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + DataLakeFileClient fileClient = InstrumentClient(new DataLakeFileClient( + dataLakeUriBuilder.ToUri(), constants.Sas.SharedKeyCredential, GetOptions())); - Assert.IsTrue(file2.CanGenerateSasUri); - // Act - DataLakeFileClient(Uri blobContainerUri, TokenCredential credential, fileClientOptions options = default) - var tokenCredentials = new DefaultAzureCredential(); - DataLakeFileClient file3 = InstrumentClient(new DataLakeFileClient( - blobEndpoint, - tokenCredentials, - GetOptions())); - Assert.IsFalse(file3.CanGenerateSasUri); + DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName, + Path = GetNewFileName(), // different path + }; + + // Act + TestHelper.AssertExpectedException( + () => fileClient.GenerateSasUri(sasBuilder), + new InvalidOperationException("SAS Uri cannot be generated. DataLakeSasBuilder.Path does not match Path in the Client. DataLakeSasBuilder.Path must either be left empty or match the Path in the Client")); } [RecordedTest] - public void CanGenerateSas_Mockable() + public void GenerateSas_BuilderIsDirectoryError() { - // Act - var file = new Mock(); - file.Setup(x => x.CanGenerateSasUri).Returns(false); + TestConstants constants = TestConstants.Create(this); + string fileSystemName = GetNewFileSystemName(); + string fileName = GetNewFileName(); + Uri serviceUri = new Uri($"https://{constants.Sas.Account}.dfs.core.windows.net"); + DataLakeUriBuilder dataLakeUriBuilder = new DataLakeUriBuilder(serviceUri) + { + FileSystemName = fileSystemName, + DirectoryOrFilePath = fileName + }; + DataLakeSasPermissions permissions = DataLakeSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); - // Assert - Assert.IsFalse(file.Object.CanGenerateSasUri); + DataLakeFileClient fileClient = InstrumentClient(new DataLakeFileClient( + dataLakeUriBuilder.ToUri(), + constants.Sas.SharedKeyCredential, + GetOptions())); - // Act - file.Setup(x => x.CanGenerateSasUri).Returns(true); + DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName, + Path = GetNewFileName(), + IsDirectory = true, + IPRange = new SasIPRange(System.Net.IPAddress.None, System.Net.IPAddress.None), + ExpiresOn = Recording.UtcNow.AddHours(+1) + }; - // Assert - Assert.IsTrue(file.Object.CanGenerateSasUri); + // Act + TestHelper.AssertExpectedException( + () => fileClient.GenerateSasUri(sasBuilder), + new InvalidOperationException("SAS Uri cannot be generated. Expected builder.IsDirectory to be set to false to generate the respective SAS for the client, GetType")); } + #endregion + #region GenerateUserDelegationSasTests [RecordedTest] - public void GenerateSas_RequiredParameters() + public async Task GenerateUserDelegationSas_RequiredParameters() { // Arrange TestConstants constants = TestConstants.Create(this); @@ -5790,11 +6378,15 @@ public void GenerateSas_RequiredParameters() }; DataLakeFileClient fileClient = InstrumentClient(new DataLakeFileClient( dataLakeUriBuilder.ToUri(), - constants.Sas.SharedKeyCredential, GetOptions())); + Response userDelegationKeyResponse = await GetServiceClient_OAuth().GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: Recording.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + // Act - Uri sasUri = fileClient.GenerateSasUri(permissions, expiresOn); + Uri sasUri = fileClient.GenerateUserDelegationSasUri(permissions, expiresOn, userDelegationKey); // Assert DataLakeSasBuilder sasBuilder2 = new DataLakeSasBuilder(permissions, expiresOn) @@ -5806,13 +6398,13 @@ public void GenerateSas_RequiredParameters() { FileSystemName = fileSystemName, DirectoryOrFilePath = path, - Sas = sasBuilder2.ToSasQueryParameters(constants.Sas.SharedKeyCredential) + Sas = sasBuilder2.ToSasQueryParameters(userDelegationKey, fileClient.AccountName) }; Assert.AreEqual(expectedUri.ToUri(), sasUri); } [RecordedTest] - public void GenerateSas_Builder() + public async Task GenerateUserDelegationSas_Builder() { TestConstants constants = TestConstants.Create(this); string fileSystemName = GetNewFileSystemName(); @@ -5827,7 +6419,6 @@ public void GenerateSas_Builder() }; DataLakeFileClient fileClient = InstrumentClient(new DataLakeFileClient( dataLakeUriBuilder.ToUri(), - constants.Sas.SharedKeyCredential, GetOptions())); DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder(permissions, expiresOn) @@ -5836,8 +6427,13 @@ public void GenerateSas_Builder() Path = path }; + Response userDelegationKeyResponse = await GetServiceClient_OAuth().GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: Recording.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + // Act - Uri sasUri = fileClient.GenerateSasUri(sasBuilder); + Uri sasUri = fileClient.GenerateUserDelegationSasUri(sasBuilder, userDelegationKey); // Assert DataLakeSasBuilder sasBuilder2 = new DataLakeSasBuilder(permissions, expiresOn) @@ -5849,13 +6445,70 @@ public void GenerateSas_Builder() { FileSystemName = fileSystemName, DirectoryOrFilePath = path, - Sas = sasBuilder2.ToSasQueryParameters(constants.Sas.SharedKeyCredential) + Sas = sasBuilder2.ToSasQueryParameters(userDelegationKey, fileClient.AccountName) }; Assert.AreEqual(expectedUri.ToUri(), sasUri); } [RecordedTest] - public void GenerateSas_BuilderNullFileSystemName() + public async Task GenerateUserDelegationSas_BuilderNull() + { + TestConstants constants = TestConstants.Create(this); + string fileSystemName = GetNewFileSystemName(); + string path = GetNewFileName(); + Uri serviceUri = new Uri($"https://{constants.Sas.Account}.dfs.core.windows.net"); + DataLakeUriBuilder dataLakeUriBuilder = new DataLakeUriBuilder(serviceUri) + { + FileSystemName = fileSystemName, + DirectoryOrFilePath = path + }; + DataLakeFileClient fileClient = InstrumentClient(new DataLakeFileClient( + dataLakeUriBuilder.ToUri(), + GetOptions())); + + Response userDelegationKeyResponse = await GetServiceClient_OAuth().GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: Recording.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + + // Act + TestHelper.AssertExpectedException( + () => fileClient.GenerateUserDelegationSasUri(null, userDelegationKey), + new ArgumentNullException("builder")); + } + + [RecordedTest] + public void GenerateUserDelegationSas_UserDelegationKeyNull() + { + TestConstants constants = TestConstants.Create(this); + string fileSystemName = GetNewFileSystemName(); + string path = GetNewFileName(); + DataLakeSasPermissions permissions = DataLakeSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + Uri serviceUri = new Uri($"https://{constants.Sas.Account}.dfs.core.windows.net"); + DataLakeUriBuilder dataLakeUriBuilder = new DataLakeUriBuilder(serviceUri) + { + FileSystemName = fileSystemName, + DirectoryOrFilePath = path + }; + DataLakeFileClient fileClient = InstrumentClient(new DataLakeFileClient( + dataLakeUriBuilder.ToUri(), + GetOptions())); + + DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName, + Path = path + }; + + // Act + TestHelper.AssertExpectedException( + () => fileClient.GenerateUserDelegationSasUri(sasBuilder, null), + new ArgumentNullException("userDelegationKey")); + } + + [RecordedTest] + public async Task GenerateUserDelegationSas_BuilderNullFileSystemName() { TestConstants constants = TestConstants.Create(this); string fileSystemName = GetNewFileSystemName(); @@ -5870,7 +6523,6 @@ public void GenerateSas_BuilderNullFileSystemName() }; DataLakeFileClient fileClient = InstrumentClient(new DataLakeFileClient( dataLakeUriBuilder.ToUri(), - constants.Sas.SharedKeyCredential, GetOptions())); DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder(permissions, expiresOn) @@ -5879,8 +6531,13 @@ public void GenerateSas_BuilderNullFileSystemName() Path = path }; + Response userDelegationKeyResponse = await GetServiceClient_OAuth().GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: Recording.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + // Act - Uri sasUri = fileClient.GenerateSasUri(sasBuilder); + Uri sasUri = fileClient.GenerateUserDelegationSasUri(sasBuilder, userDelegationKey); // Assert DataLakeSasBuilder sasBuilder2 = new DataLakeSasBuilder(permissions, expiresOn) @@ -5892,13 +6549,13 @@ public void GenerateSas_BuilderNullFileSystemName() { FileSystemName = fileSystemName, DirectoryOrFilePath = path, - Sas = sasBuilder2.ToSasQueryParameters(constants.Sas.SharedKeyCredential) + Sas = sasBuilder2.ToSasQueryParameters(userDelegationKey, fileClient.AccountName) }; Assert.AreEqual(expectedUri.ToUri(), sasUri); } [RecordedTest] - public void GenerateSas_BuilderWrongFileSystemName() + public async Task GenerateUserDelegationSas_BuilderWrongFileSystemName() { // Arrange TestConstants constants = TestConstants.Create(this); @@ -5914,7 +6571,6 @@ public void GenerateSas_BuilderWrongFileSystemName() DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); DataLakeFileClient fileClient = InstrumentClient(new DataLakeFileClient( dataLakeUriBuilder.ToUri(), - constants.Sas.SharedKeyCredential, GetOptions())); DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder(permissions, expiresOn) @@ -5923,14 +6579,19 @@ public void GenerateSas_BuilderWrongFileSystemName() Path = path, }; + Response userDelegationKeyResponse = await GetServiceClient_OAuth().GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: Recording.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + // Act TestHelper.AssertExpectedException( - () => fileClient.GenerateSasUri(sasBuilder), + () => fileClient.GenerateUserDelegationSasUri(sasBuilder, userDelegationKey), new InvalidOperationException("SAS Uri cannot be generated. DataLakeSasBuilder.FileSystemName does not match FileSystemName in the Client. DataLakeSasBuilder.FileSystemName must either be left empty or match the FileSystemName in the Client")); } [RecordedTest] - public void GenerateSas_BuilderNullFileName() + public async Task GenerateUserDelegationSas_BuilderNullFileName() { TestConstants constants = TestConstants.Create(this); string fileSystemName = GetNewFileSystemName(); @@ -5945,7 +6606,6 @@ public void GenerateSas_BuilderNullFileName() }; DataLakeFileClient fileClient = InstrumentClient(new DataLakeFileClient( dataLakeUriBuilder.ToUri(), - constants.Sas.SharedKeyCredential, GetOptions())); DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder(permissions, expiresOn) @@ -5954,8 +6614,13 @@ public void GenerateSas_BuilderNullFileName() Path = null }; + Response userDelegationKeyResponse = await GetServiceClient_OAuth().GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: Recording.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + // Act - Uri sasUri = fileClient.GenerateSasUri(sasBuilder); + Uri sasUri = fileClient.GenerateUserDelegationSasUri(sasBuilder, userDelegationKey); // Assert DataLakeSasBuilder sasBuilder2 = new DataLakeSasBuilder(permissions, expiresOn) @@ -5967,13 +6632,13 @@ public void GenerateSas_BuilderNullFileName() { FileSystemName = fileSystemName, DirectoryOrFilePath = path, - Sas = sasBuilder2.ToSasQueryParameters(constants.Sas.SharedKeyCredential) + Sas = sasBuilder2.ToSasQueryParameters(userDelegationKey, fileClient.AccountName) }; Assert.AreEqual(expectedUri.ToUri(), sasUri); } [RecordedTest] - public void GenerateSas_BuilderWrongFileName() + public async Task GenerateUserDelegationSas_BuilderWrongFileName() { // Arrange TestConstants constants = TestConstants.Create(this); @@ -5989,7 +6654,6 @@ public void GenerateSas_BuilderWrongFileName() DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); DataLakeFileClient fileClient = InstrumentClient(new DataLakeFileClient( dataLakeUriBuilder.ToUri(), - constants.Sas.SharedKeyCredential, GetOptions())); DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder(permissions, expiresOn) @@ -5998,14 +6662,68 @@ public void GenerateSas_BuilderWrongFileName() Path = GetNewFileName(), // different path }; + Response userDelegationKeyResponse = await GetServiceClient_OAuth().GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: Recording.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + // Act TestHelper.AssertExpectedException( - () => fileClient.GenerateSasUri(sasBuilder), + () => fileClient.GenerateUserDelegationSasUri(sasBuilder, userDelegationKey), new InvalidOperationException("SAS Uri cannot be generated. DataLakeSasBuilder.Path does not match Path in the Client. DataLakeSasBuilder.Path must either be left empty or match the Path in the Client")); } [RecordedTest] - public void GenerateSas_BuilderIsDirectoryError() + public async Task GenerateUserDelegationSas_BuilderNullIsDirectory() + { + TestConstants constants = TestConstants.Create(this); + string fileSystemName = GetNewFileSystemName(); + string path = GetNewFileName(); + DataLakeSasPermissions permissions = DataLakeSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + Uri serviceUri = new Uri($"https://{constants.Sas.Account}.dfs.core.windows.net"); + DataLakeUriBuilder dataLakeUriBuilder = new DataLakeUriBuilder(serviceUri) + { + FileSystemName = fileSystemName, + DirectoryOrFilePath = path + }; + DataLakeFileClient fileClient = InstrumentClient(new DataLakeFileClient( + dataLakeUriBuilder.ToUri(), + GetOptions())); + + DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName, + Path = path, + IsDirectory = null + }; + + Response userDelegationKeyResponse = await GetServiceClient_OAuth().GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: Recording.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + + // Act + Uri sasUri = fileClient.GenerateUserDelegationSasUri(sasBuilder, userDelegationKey); + + // Assert + DataLakeSasBuilder sasBuilder2 = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName, + Path = path, + IsDirectory = false + }; + DataLakeUriBuilder expectedUri = new DataLakeUriBuilder(serviceUri) + { + FileSystemName = fileSystemName, + DirectoryOrFilePath = path, + Sas = sasBuilder2.ToSasQueryParameters(userDelegationKey, fileClient.AccountName) + }; + Assert.AreEqual(expectedUri.ToUri(), sasUri); + } + + [RecordedTest] + public async Task GenerateUserDelegationSas_BuilderIsDirectoryError() { TestConstants constants = TestConstants.Create(this); string fileSystemName = GetNewFileSystemName(); @@ -6021,7 +6739,6 @@ public void GenerateSas_BuilderIsDirectoryError() DataLakeFileClient fileClient = InstrumentClient(new DataLakeFileClient( dataLakeUriBuilder.ToUri(), - constants.Sas.SharedKeyCredential, GetOptions())); DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder(permissions, expiresOn) @@ -6033,10 +6750,15 @@ public void GenerateSas_BuilderIsDirectoryError() ExpiresOn = Recording.UtcNow.AddHours(+1) }; + Response userDelegationKeyResponse = await GetServiceClient_OAuth().GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: Recording.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + // Act TestHelper.AssertExpectedException( - () => fileClient.GenerateSasUri(sasBuilder), - new InvalidOperationException("SAS Uri cannot be generated. Expected builder.IsDirectory to be set to FalseName to generatethe respective SAS for the client, GetType")); + () => fileClient.GenerateUserDelegationSasUri(sasBuilder, userDelegationKey), + new InvalidOperationException("SAS Uri cannot be generated. Expected builder.IsDirectory to be set to false to generate the respective SAS for the client, GetType")); } #endregion diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/FileSystemClientTests.cs b/sdk/storage/Azure.Storage.Files.DataLake/tests/FileSystemClientTests.cs index 2c0af9ceaf4eb..6f719cb694eba 100644 --- a/sdk/storage/Azure.Storage.Files.DataLake/tests/FileSystemClientTests.cs +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/FileSystemClientTests.cs @@ -3329,6 +3329,272 @@ public void GenerateSas_BuilderWrongName() } #endregion + #region GenerateUserDelegationSasTests + [RecordedTest] + public async Task GenerateUserDelegationSas_RequiredParameters() + { + // Arrange + TestConstants constants = TestConstants.Create(this); + string fileSystemName = GetNewFileSystemName(); + DataLakeFileSystemSasPermissions permissions = DataLakeFileSystemSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + + Uri serviceUri = new Uri($"https://{constants.Sas.Account}.dfs.core.windows.net"); + DataLakeUriBuilder dataLakeUriBuilder = new DataLakeUriBuilder(serviceUri) + { + FileSystemName = fileSystemName + }; + + DataLakeFileSystemClient fileSystemClient = InstrumentClient(new DataLakeFileSystemClient( + dataLakeUriBuilder.ToUri(), + GetOptions())); + + string stringToSign = null; + Response userDelegationKeyResponse = await GetServiceClient_OAuth().GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: Recording.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + + // Act + Uri sasUri = fileSystemClient.GenerateUserDelegationSasUri(permissions, expiresOn, userDelegationKey, out stringToSign); + + // Assert + DataLakeSasBuilder sasBuilder2 = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName + }; + DataLakeUriBuilder expectedUri = new DataLakeUriBuilder(serviceUri) + { + FileSystemName = fileSystemName, + Sas = sasBuilder2.ToSasQueryParameters(userDelegationKey, fileSystemClient.AccountName) + }; + Assert.AreEqual(expectedUri.ToUri(), sasUri); + Assert.IsNotNull(stringToSign); + } + + [RecordedTest] + public async Task GenerateUserDelegationSas_Builder() + { + var constants = TestConstants.Create(this); + string fileSystemName = GetNewFileSystemName(); + DataLakeFileSystemSasPermissions permissions = DataLakeFileSystemSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + + Uri serviceUri = new Uri($"https://{constants.Sas.Account}.dfs.core.windows.net"); + DataLakeUriBuilder dataLakeUriBuilder = new DataLakeUriBuilder(serviceUri) + { + FileSystemName = fileSystemName + }; + + DataLakeFileSystemClient fileSystemClient = InstrumentClient(new DataLakeFileSystemClient( + dataLakeUriBuilder.ToUri(), + GetOptions())); + + DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemClient.Name + }; + + string stringToSign = null; + Response userDelegationKeyResponse = await GetServiceClient_OAuth().GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: Recording.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + + // Act + Uri sasUri = fileSystemClient.GenerateUserDelegationSasUri(sasBuilder, userDelegationKey, out stringToSign); + + // Assert + DataLakeSasBuilder sasBuilder2 = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName + }; + DataLakeUriBuilder expectedUri = new DataLakeUriBuilder(serviceUri) + { + FileSystemName = fileSystemName, + Sas = sasBuilder2.ToSasQueryParameters(userDelegationKey, fileSystemClient.AccountName) + }; + + Assert.AreEqual(expectedUri.ToUri(), sasUri); + Assert.IsNotNull(stringToSign); + } + + [RecordedTest] + public async Task GenerateUserDelegationSas_BuilderNull() + { + var constants = TestConstants.Create(this); + string fileSystemName = GetNewFileSystemName(); + + Uri serviceUri = new Uri($"https://{constants.Sas.Account}.dfs.core.windows.net"); + DataLakeUriBuilder dataLakeUriBuilder = new DataLakeUriBuilder(serviceUri) + { + FileSystemName = fileSystemName + }; + + DataLakeFileSystemClient fileSystemClient = InstrumentClient(new DataLakeFileSystemClient( + dataLakeUriBuilder.ToUri(), + GetOptions())); + + string stringToSign = null; + Response userDelegationKeyResponse = await GetServiceClient_OAuth().GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: Recording.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + + // Act + TestHelper.AssertExpectedException( + () => fileSystemClient.GenerateUserDelegationSasUri(null, userDelegationKey, out stringToSign), + new ArgumentNullException("builder")); + } + + [RecordedTest] + public void GenerateUserDelegationSas_UserDelegationKeyNull() + { + var constants = TestConstants.Create(this); + string fileSystemName = GetNewFileSystemName(); + DataLakeFileSystemSasPermissions permissions = DataLakeFileSystemSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + + Uri serviceUri = new Uri($"https://{constants.Sas.Account}.dfs.core.windows.net"); + DataLakeUriBuilder dataLakeUriBuilder = new DataLakeUriBuilder(serviceUri) + { + FileSystemName = fileSystemName + }; + + DataLakeFileSystemClient fileSystemClient = InstrumentClient(new DataLakeFileSystemClient( + dataLakeUriBuilder.ToUri(), + GetOptions())); + + DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemClient.Name + }; + + string stringToSign = null; + + // Act + TestHelper.AssertExpectedException( + () => fileSystemClient.GenerateUserDelegationSasUri(sasBuilder, null, out stringToSign), + new ArgumentNullException("userDelegationKey")); + } + + [RecordedTest] + public async Task GenerateUserDelegationSas_BuilderNullName() + { + var constants = TestConstants.Create(this); + string fileSystemName = GetNewFileSystemName(); + DataLakeFileSystemSasPermissions permissions = DataLakeFileSystemSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + + Uri serviceUri = new Uri($"https://{constants.Sas.Account}.dfs.core.windows.net"); + DataLakeUriBuilder dataLakeUriBuilder = new DataLakeUriBuilder(serviceUri) + { + FileSystemName = fileSystemName + }; + + DataLakeFileSystemClient fileSystemClient = InstrumentClient(new DataLakeFileSystemClient( + dataLakeUriBuilder.ToUri(), + GetOptions())); + + DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = null + }; + + Response userDelegationKeyResponse = await GetServiceClient_OAuth().GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: Recording.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + + // Act + Uri sasUri = fileSystemClient.GenerateUserDelegationSasUri(sasBuilder, userDelegationKey); + + // Assert + DataLakeSasBuilder sasBuilder2 = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName + }; + DataLakeUriBuilder expectedUri = new DataLakeUriBuilder(serviceUri) + { + FileSystemName = fileSystemName, + Sas = sasBuilder2.ToSasQueryParameters(userDelegationKey, fileSystemClient.AccountName) + }; + + Assert.AreEqual(expectedUri.ToUri(), sasUri); + } + + [RecordedTest] + public async Task GenerateUserDelegationSas_BuilderWrongName() + { + // Arrange + TestConstants constants = TestConstants.Create(this); + string fileSystemName = GetNewFileSystemName(); + + Uri serviceUri = new Uri($"https://{constants.Sas.Account}.dfs.core.windows.net"); + DataLakeUriBuilder dataLakeUriBuilder = new DataLakeUriBuilder(serviceUri) + { + FileSystemName = fileSystemName + }; + + DataLakeFileSystemSasPermissions permissions = DataLakeFileSystemSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + DataLakeFileSystemClient fileSystemClient = InstrumentClient(new DataLakeFileSystemClient( + dataLakeUriBuilder.ToUri(), + GetOptions())); + + DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = GetNewFileSystemName(), // different filesytem name + }; + + Response userDelegationKeyResponse = await GetServiceClient_OAuth().GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: Recording.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + + // Act + TestHelper.AssertExpectedException( + () => fileSystemClient.GenerateUserDelegationSasUri(sasBuilder, userDelegationKey), + new InvalidOperationException("SAS Uri cannot be generated. DataLakeSasBuilder.FileSystemName does not match Name in the Client. DataLakeSasBuilder.FileSystemName must either be left empty or match the Name in the Client")); + } + + [RecordedTest] + public async Task GenerateUserDelegationSas_BuilderIncorrectlySetPath() + { + // Arrange + TestConstants constants = TestConstants.Create(this); + string fileSystemName = GetNewFileSystemName(); + + Uri serviceUri = new Uri($"https://{constants.Sas.Account}.dfs.core.windows.net"); + DataLakeUriBuilder dataLakeUriBuilder = new DataLakeUriBuilder(serviceUri) + { + FileSystemName = fileSystemName + }; + + DataLakeFileSystemSasPermissions permissions = DataLakeFileSystemSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + DataLakeFileSystemClient fileSystemClient = InstrumentClient(new DataLakeFileSystemClient( + dataLakeUriBuilder.ToUri(), + GetOptions())); + + DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName, + Path = GetNewFileName() + }; + + Response userDelegationKeyResponse = await GetServiceClient_OAuth().GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: Recording.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + + // Act + TestHelper.AssertExpectedException( + () => fileSystemClient.GenerateUserDelegationSasUri(sasBuilder, userDelegationKey), + new InvalidOperationException("SAS Uri cannot be generated. builder.Path cannot be set to create a FileSystemName SAS.")); + } + #endregion + [RecordedTest] public void CanMockClientConstructors() { diff --git a/sdk/storage/Azure.Storage.Files.DataLake/tests/PathClientTests.cs b/sdk/storage/Azure.Storage.Files.DataLake/tests/PathClientTests.cs index 64d119eb3c860..f95ecaeac8d59 100644 --- a/sdk/storage/Azure.Storage.Files.DataLake/tests/PathClientTests.cs +++ b/sdk/storage/Azure.Storage.Files.DataLake/tests/PathClientTests.cs @@ -409,6 +409,368 @@ public void GenerateSas_BuilderIsDirectoryError() } #endregion + #region GenerateUserDelegationSasTests + [RecordedTest] + public async Task GenerateUserDelegationSas_RequiredParameters() + { + // Arrange + var constants = TestConstants.Create(this); + string fileSystemName = GetNewFileSystemName(); + string path = GetNewFileName(); + DataLakeSasPermissions permissions = DataLakeSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + var blobEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account + "/" + fileSystemName + "/" + path); + DataLakePathClient pathClient = InstrumentClient(new DataLakePathClient( + blobEndpoint, + GetOptions())); + + string stringToSign = null; + Response userDelegationKeyResponse = await GetServiceClient_OAuth().GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: Recording.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + + // Act + Uri sasUri = pathClient.GenerateUserDelegationSasUri(permissions, expiresOn, userDelegationKey, out stringToSign); + + // Assert + DataLakeSasBuilder sasBuilder2 = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName, + Path = path + }; + DataLakeUriBuilder expectedUri = new DataLakeUriBuilder(blobEndpoint) + { + Sas = sasBuilder2.ToSasQueryParameters(userDelegationKey, pathClient.AccountName) + }; + Assert.AreEqual(expectedUri.ToUri().ToString(), sasUri.ToString()); + Assert.IsNotNull(stringToSign); + } + + [RecordedTest] + public async Task GenerateUserDelegationSas_Builder() + { + var constants = TestConstants.Create(this); + string fileSystemName = GetNewFileSystemName(); + string path = GetNewFileName(); + DataLakeSasPermissions permissions = DataLakeSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + DateTimeOffset startsOn = Recording.UtcNow.AddHours(-1); + var blobEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account + "/" + fileSystemName + "/" + path); + DataLakePathClient pathClient = InstrumentClient(new DataLakePathClient( + blobEndpoint, + GetOptions())); + + DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName, + Path = path, + StartsOn = startsOn + }; + + string stringToSign = null; + Response userDelegationKeyResponse = await GetServiceClient_OAuth().GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: Recording.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + + // Act + Uri sasUri = pathClient.GenerateUserDelegationSasUri(sasBuilder, userDelegationKey, out stringToSign); + + // Assert + DataLakeUriBuilder expectedUri = new DataLakeUriBuilder(blobEndpoint); + DataLakeSasBuilder sasBuilder2 = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName, + Path = path, + StartsOn = startsOn + }; + expectedUri.Sas = sasBuilder2.ToSasQueryParameters(userDelegationKey, pathClient.AccountName); + Assert.AreEqual(expectedUri.ToUri().ToString(), sasUri.ToString()); + Assert.IsNotNull(stringToSign); + } + + [RecordedTest] + public async Task GenerateUserDelegationSas_BuilderNull() + { + var constants = TestConstants.Create(this); + string fileSystemName = GetNewFileSystemName(); + string path = GetNewFileName(); + var blobEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account + "/" + fileSystemName + "/" + path); + DataLakePathClient pathClient = InstrumentClient(new DataLakePathClient( + blobEndpoint, + GetOptions())); + + string stringToSign = null; + Response userDelegationKeyResponse = await GetServiceClient_OAuth().GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: Recording.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + + // Act + TestHelper.AssertExpectedException( + () => pathClient.GenerateUserDelegationSasUri(null, userDelegationKey, out stringToSign), + new ArgumentNullException("builder")); + } + + [RecordedTest] + public void GenerateUserDelegationSas_UserDelegationKeyNull() + { + var constants = TestConstants.Create(this); + string fileSystemName = GetNewFileSystemName(); + string path = GetNewFileName(); + DataLakeSasPermissions permissions = DataLakeSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + DateTimeOffset startsOn = Recording.UtcNow.AddHours(-1); + var blobEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account + "/" + fileSystemName + "/" + path); + DataLakePathClient pathClient = InstrumentClient(new DataLakePathClient( + blobEndpoint, + GetOptions())); + + DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName, + Path = path, + StartsOn = startsOn + }; + + string stringToSign = null; + + // Act + TestHelper.AssertExpectedException( + () => pathClient.GenerateUserDelegationSasUri(sasBuilder, null, out stringToSign), + new ArgumentNullException("userDelegationKey")); + } + + [RecordedTest] + public async Task GenerateUserDelegationSas_BuilderNullFileSystemName() + { + var constants = TestConstants.Create(this); + string fileSystemName = GetNewFileSystemName(); + string path = GetNewFileName(); + DataLakeSasPermissions permissions = DataLakeSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + DateTimeOffset startsOn = Recording.UtcNow.AddHours(-1); + var blobEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account + "/" + fileSystemName + "/" + path); + DataLakePathClient pathClient = InstrumentClient(new DataLakePathClient( + blobEndpoint, + GetOptions())); + + DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = null, + Path = path, + StartsOn = startsOn + }; + + string stringToSign = null; + Response userDelegationKeyResponse = await GetServiceClient_OAuth().GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: Recording.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + + // Act + Uri sasUri = pathClient.GenerateUserDelegationSasUri(sasBuilder, userDelegationKey, out stringToSign); + + // Assert + DataLakeUriBuilder expectedUri = new DataLakeUriBuilder(blobEndpoint); + DataLakeSasBuilder sasBuilder2 = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName, + Path = path, + StartsOn = startsOn + }; + expectedUri.Sas = sasBuilder2.ToSasQueryParameters(userDelegationKey, pathClient.AccountName); + Assert.AreEqual(expectedUri.ToUri().ToString(), sasUri.ToString()); + Assert.IsNotNull(stringToSign); + } + + [RecordedTest] + public async Task GenerateUserDelegationSas_BuilderWrongFileSystemName() + { + // Arrange + var constants = TestConstants.Create(this); + var blobEndpoint = new Uri("http://127.0.0.1/"); + UriBuilder blobUriBuilder = new UriBuilder(blobEndpoint); + string path = GetNewFileName(); + DataLakeSasPermissions permissions = DataLakeSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + blobUriBuilder.Path += constants.Sas.Account + "/" + GetNewFileSystemName() + "/" + path; + DataLakePathClient pathClient = InstrumentClient(new DataLakePathClient( + blobUriBuilder.Uri, + GetOptions())); + + DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = GetNewFileSystemName(), // different filesystem name + Path = path, + }; + + Response userDelegationKeyResponse = await GetServiceClient_OAuth().GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: Recording.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + + // Act + TestHelper.AssertExpectedException( + () => pathClient.GenerateUserDelegationSasUri(sasBuilder, userDelegationKey), + new InvalidOperationException("SAS Uri cannot be generated. DataLakeSasBuilder.FileSystemName does not match FileSystemName in the Client. DataLakeSasBuilder.FileSystemName must either be left empty or match the FileSystemName in the Client")); + } + + [RecordedTest] + public async Task GenerateUserDelegationSas_BuilderNullPath() + { + var constants = TestConstants.Create(this); + string fileSystemName = GetNewFileSystemName(); + string path = GetNewFileName(); + DataLakeSasPermissions permissions = DataLakeSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + DateTimeOffset startsOn = Recording.UtcNow.AddHours(-1); + var blobEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account + "/" + fileSystemName + "/" + path); + DataLakePathClient pathClient = InstrumentClient(new DataLakePathClient( + blobEndpoint, + GetOptions())); + + DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName, + Path = null, + StartsOn = startsOn + }; + + string stringToSign = null; + Response userDelegationKeyResponse = await GetServiceClient_OAuth().GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: Recording.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + + // Act + Uri sasUri = pathClient.GenerateUserDelegationSasUri(sasBuilder, userDelegationKey, out stringToSign); + + // Assert + DataLakeUriBuilder expectedUri = new DataLakeUriBuilder(blobEndpoint); + DataLakeSasBuilder sasBuilder2 = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName, + Path = path, + StartsOn = startsOn + }; + expectedUri.Sas = sasBuilder2.ToSasQueryParameters(userDelegationKey, pathClient.AccountName); + Assert.AreEqual(expectedUri.ToUri().ToString(), sasUri.ToString()); + Assert.IsNotNull(stringToSign); + } + + [RecordedTest] + public async Task GenerateUserDelegationSas_BuilderWrongPath() + { + // Arrange + var constants = TestConstants.Create(this); + var blobEndpoint = new Uri("http://127.0.0.1/"); + UriBuilder blobUriBuilder = new UriBuilder(blobEndpoint); + string fileSystemName = GetNewFileSystemName(); + DataLakeSasPermissions permissions = DataLakeSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + blobUriBuilder.Path += constants.Sas.Account + "/" + fileSystemName + "/" + GetNewFileName(); + DataLakePathClient pathClient = InstrumentClient(new DataLakePathClient( + blobUriBuilder.Uri, + GetOptions())); + + DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName, + Path = GetNewFileName(), // different path + }; + + Response userDelegationKeyResponse = await GetServiceClient_OAuth().GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: Recording.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + + // Act + TestHelper.AssertExpectedException( + () => pathClient.GenerateUserDelegationSasUri(sasBuilder, userDelegationKey), + new InvalidOperationException("SAS Uri cannot be generated. DataLakeSasBuilder.Path does not match Path in the Client. DataLakeSasBuilder.Path must either be left empty or match the Path in the Client")); + } + + [RecordedTest] + public async Task GenerateUserDelegationSas_BuilderNullIsDirectory() + { + var constants = TestConstants.Create(this); + string fileSystemName = GetNewFileSystemName(); + string path = GetNewFileName(); + DataLakeSasPermissions permissions = DataLakeSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + DateTimeOffset startsOn = Recording.UtcNow.AddHours(-1); + var blobEndpoint = new Uri("http://127.0.0.1/" + constants.Sas.Account + "/" + fileSystemName + "/" + path); + DataLakePathClient pathClient = InstrumentClient(new DataLakePathClient( + blobEndpoint, + GetOptions())); + + DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName, + Path = path, + StartsOn = startsOn, + IsDirectory = null + }; + + string stringToSign = null; + Response userDelegationKeyResponse = await GetServiceClient_OAuth().GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: Recording.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + + // Act + Uri sasUri = pathClient.GenerateUserDelegationSasUri(sasBuilder, userDelegationKey, out stringToSign); + + // Assert + DataLakeUriBuilder expectedUri = new DataLakeUriBuilder(blobEndpoint); + DataLakeSasBuilder sasBuilder2 = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName, + Path = path, + StartsOn = startsOn, + IsDirectory = false + }; + expectedUri.Sas = sasBuilder2.ToSasQueryParameters(userDelegationKey, pathClient.AccountName); + Assert.AreEqual(expectedUri.ToUri().ToString(), sasUri.ToString()); + Assert.IsNotNull(stringToSign); + } + + [RecordedTest] + public async Task GenerateUserDelegationSas_BuilderIsDirectoryError() + { + var constants = TestConstants.Create(this); + var blobEndpoint = new Uri("http://127.0.0.1/"); + UriBuilder blobUriBuilder = new UriBuilder(blobEndpoint); + string fileSystemName = GetNewFileSystemName(); + string fileName = GetNewFileName(); + DataLakeSasPermissions permissions = DataLakeSasPermissions.Read; + DateTimeOffset expiresOn = Recording.UtcNow.AddHours(+1); + blobUriBuilder.Path += constants.Sas.Account + "/" + fileSystemName + "/" + fileName; + DataLakePathClient pathClient = InstrumentClient(new DataLakePathClient( + blobUriBuilder.Uri, + GetOptions())); + + DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder(permissions, expiresOn) + { + FileSystemName = fileSystemName, + Path = fileName, + IsDirectory = true, + }; + + Response userDelegationKeyResponse = await GetServiceClient_OAuth().GetUserDelegationKeyAsync( + startsOn: null, + expiresOn: Recording.UtcNow.AddHours(1)); + UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value; + + // Act + TestHelper.AssertExpectedException( + () => pathClient.GenerateUserDelegationSasUri(sasBuilder, userDelegationKey), + new InvalidOperationException("SAS Uri cannot be generated. Expected builder.IsDirectory to be set to false to generate the respective SAS for the client, GetType")); + } + #endregion + [RecordedTest] public void CanMockClientConstructors() { diff --git a/sdk/storage/Azure.Storage.Files.Shares/CHANGELOG.md b/sdk/storage/Azure.Storage.Files.Shares/CHANGELOG.md index d4dca5b45c76f..554b55af32ad5 100644 --- a/sdk/storage/Azure.Storage.Files.Shares/CHANGELOG.md +++ b/sdk/storage/Azure.Storage.Files.Shares/CHANGELOG.md @@ -3,12 +3,10 @@ ## 12.21.0-beta.1 (Unreleased) ### Features Added - -### Breaking Changes - -### Bugs Fixed - -### Other Changes +- Added support for service version 2025-01-05. +- Added support for the provisioned V2 billing model. +- Added support for specifying the binary file permission format for ShareFileClient.StartCopy() and .StartCopyAsync(). +- Added ShareAccessTier.Premium enum value. ## 12.20.0 (2024-09-18) diff --git a/sdk/storage/Azure.Storage.Files.Shares/api/Azure.Storage.Files.Shares.net6.0.cs b/sdk/storage/Azure.Storage.Files.Shares/api/Azure.Storage.Files.Shares.net6.0.cs index 4c5f277eb6166..b1b355dda471c 100644 --- a/sdk/storage/Azure.Storage.Files.Shares/api/Azure.Storage.Files.Shares.net6.0.cs +++ b/sdk/storage/Azure.Storage.Files.Shares/api/Azure.Storage.Files.Shares.net6.0.cs @@ -25,11 +25,12 @@ public ShareClient(System.Uri shareUri, Azure.Storage.StorageSharedKeyCredential public virtual System.Threading.Tasks.Task> CreateDirectoryAsync(string directoryName, Azure.Storage.Files.Shares.Models.ShareDirectoryCreateOptions options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public virtual System.Threading.Tasks.Task> CreateDirectoryAsync(string directoryName, System.Collections.Generic.IDictionary metadata, Azure.Storage.Files.Shares.Models.FileSmbProperties smbProperties, string filePermission, System.Threading.CancellationToken cancellationToken) { throw null; } - public virtual Azure.Response CreateIfNotExists(Azure.Storage.Files.Shares.Models.ShareCreateOptions options, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual Azure.Response CreateIfNotExists(Azure.Storage.Files.Shares.Models.ShareCreateOptions options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] - public virtual Azure.Response CreateIfNotExists(System.Collections.Generic.IDictionary metadata = null, int? quotaInGB = default(int?), System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } - public virtual System.Threading.Tasks.Task> CreateIfNotExistsAsync(Azure.Storage.Files.Shares.Models.ShareCreateOptions options, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } - public virtual System.Threading.Tasks.Task> CreateIfNotExistsAsync(System.Collections.Generic.IDictionary metadata = null, int? quotaInGB = default(int?), System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual Azure.Response CreateIfNotExists(System.Collections.Generic.IDictionary metadata, int? quotaInGB, System.Threading.CancellationToken cancellationToken) { throw null; } + public virtual System.Threading.Tasks.Task> CreateIfNotExistsAsync(Azure.Storage.Files.Shares.Models.ShareCreateOptions options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public virtual System.Threading.Tasks.Task> CreateIfNotExistsAsync(System.Collections.Generic.IDictionary metadata, int? quotaInGB, System.Threading.CancellationToken cancellationToken) { throw null; } public virtual Azure.Response CreatePermission(Azure.Storage.Files.Shares.Models.ShareFilePermission permission, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public virtual Azure.Response CreatePermission(string permission, System.Threading.CancellationToken cancellationToken) { throw null; } @@ -114,7 +115,7 @@ public ShareClient(System.Uri shareUri, Azure.Storage.StorageSharedKeyCredential } public partial class ShareClientOptions : Azure.Core.ClientOptions { - public ShareClientOptions(Azure.Storage.Files.Shares.ShareClientOptions.ServiceVersion version = Azure.Storage.Files.Shares.ShareClientOptions.ServiceVersion.V2024_11_04) { } + public ShareClientOptions(Azure.Storage.Files.Shares.ShareClientOptions.ServiceVersion version = Azure.Storage.Files.Shares.ShareClientOptions.ServiceVersion.V2025_01_05) { } public bool? AllowSourceTrailingDot { get { throw null; } set { } } public bool? AllowTrailingDot { get { throw null; } set { } } public Azure.Storage.Files.Shares.Models.ShareAudience? Audience { get { throw null; } set { } } @@ -147,6 +148,7 @@ public enum ServiceVersion V2024_05_04 = 22, V2024_08_04 = 23, V2024_11_04 = 24, + V2025_01_05 = 25, } } public partial class ShareDirectoryClient @@ -562,6 +564,7 @@ public ShareAccessPolicy() { } public ShareAccessTier(string value) { throw null; } public static Azure.Storage.Files.Shares.Models.ShareAccessTier Cool { get { throw null; } } public static Azure.Storage.Files.Shares.Models.ShareAccessTier Hot { get { throw null; } } + public static Azure.Storage.Files.Shares.Models.ShareAccessTier Premium { get { throw null; } } public static Azure.Storage.Files.Shares.Models.ShareAccessTier TransactionOptimized { get { throw null; } } public bool Equals(Azure.Storage.Files.Shares.Models.ShareAccessTier other) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] @@ -610,6 +613,8 @@ public ShareCreateOptions() { } public long? PaidBurstingMaxBandwidthMibps { get { throw null; } set { } } public long? PaidBurstingMaxIops { get { throw null; } set { } } public Azure.Storage.Files.Shares.Models.ShareProtocols? Protocols { get { throw null; } set { } } + public long? ProvisionedMaxBandwidthMibps { get { throw null; } set { } } + public long? ProvisionedMaxIops { get { throw null; } set { } } public int? QuotaInGB { get { throw null; } set { } } public Azure.Storage.Files.Shares.Models.ShareRootSquash? RootSquash { get { throw null; } set { } } } @@ -681,6 +686,8 @@ public ShareDirectorySetHttpHeadersOptions() { } public static Azure.Storage.Files.Shares.Models.ShareErrorCode EmptyMetadataKey { get { throw null; } } public static Azure.Storage.Files.Shares.Models.ShareErrorCode FeatureVersionMismatch { get { throw null; } } public static Azure.Storage.Files.Shares.Models.ShareErrorCode FileLockConflict { get { throw null; } } + public static Azure.Storage.Files.Shares.Models.ShareErrorCode FileShareProvisionedBandwidthDowngradeNotAllowed { get { throw null; } } + public static Azure.Storage.Files.Shares.Models.ShareErrorCode FileShareProvisionedIopsDowngradeNotAllowed { get { throw null; } } public static Azure.Storage.Files.Shares.Models.ShareErrorCode InsufficientAccountPermissions { get { throw null; } } public static Azure.Storage.Files.Shares.Models.ShareErrorCode InternalError { get { throw null; } } public static Azure.Storage.Files.Shares.Models.ShareErrorCode InvalidAuthenticationInfo { get { throw null; } } @@ -760,6 +767,7 @@ public ShareFileCopyOptions() { } public Azure.Storage.Files.Shares.Models.PermissionCopyMode? FilePermissionCopyMode { get { throw null; } set { } } public bool? IgnoreReadOnly { get { throw null; } set { } } public System.Collections.Generic.IDictionary Metadata { get { throw null; } set { } } + public Azure.Storage.Files.Shares.Models.FilePermissionFormat? PermissionFormat { get { throw null; } set { } } public Azure.Storage.Files.Shares.Models.FileSmbProperties SmbProperties { get { throw null; } set { } } public Azure.Storage.Files.Shares.Models.CopyableFileSmbProperties SmbPropertiesToCopy { get { throw null; } set { } } } @@ -1115,7 +1123,9 @@ public static partial class ShareModelFactory public static Azure.Storage.Files.Shares.Models.ShareProperties ShareProperties(string accessTier = null, System.DateTimeOffset? lastModified = default(System.DateTimeOffset?), int? provisionedIops = default(int?), int? provisionedIngressMBps = default(int?), int? provisionedEgressMBps = default(int?), System.DateTimeOffset? nextAllowedQuotaDowngradeTime = default(System.DateTimeOffset?), System.DateTimeOffset? deletedOn = default(System.DateTimeOffset?), int? remainingRetentionDays = default(int?), Azure.ETag? eTag = default(Azure.ETag?), System.DateTimeOffset? accessTierChangeTime = default(System.DateTimeOffset?), string accessTierTransitionState = null, Azure.Storage.Files.Shares.Models.ShareLeaseStatus? leaseStatus = default(Azure.Storage.Files.Shares.Models.ShareLeaseStatus?), Azure.Storage.Files.Shares.Models.ShareLeaseState? leaseState = default(Azure.Storage.Files.Shares.Models.ShareLeaseState?), Azure.Storage.Files.Shares.Models.ShareLeaseDuration? leaseDuration = default(Azure.Storage.Files.Shares.Models.ShareLeaseDuration?), int? quotaInGB = default(int?), System.Collections.Generic.IDictionary metadata = null, Azure.Storage.Files.Shares.Models.ShareProtocols? protocols = default(Azure.Storage.Files.Shares.Models.ShareProtocols?), Azure.Storage.Files.Shares.Models.ShareRootSquash? rootSquash = default(Azure.Storage.Files.Shares.Models.ShareRootSquash?)) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public static Azure.Storage.Files.Shares.Models.ShareProperties ShareProperties(string accessTier, System.DateTimeOffset? lastModified, int? provisionedIops, int? provisionedIngressMBps, int? provisionedEgressMBps, System.DateTimeOffset? nextAllowedQuotaDowngradeTime, System.DateTimeOffset? deletedOn, int? remainingRetentionDays, Azure.ETag? eTag, System.DateTimeOffset? accessTierChangeTime, string accessTierTransitionState, Azure.Storage.Files.Shares.Models.ShareLeaseStatus? leaseStatus, Azure.Storage.Files.Shares.Models.ShareLeaseState? leaseState, Azure.Storage.Files.Shares.Models.ShareLeaseDuration? leaseDuration, int? quotaInGB, System.Collections.Generic.IDictionary metadata, Azure.Storage.Files.Shares.Models.ShareProtocols? protocols, Azure.Storage.Files.Shares.Models.ShareRootSquash? rootSquash, bool? enableSnapshotVirtualDirectoryAccess) { throw null; } - public static Azure.Storage.Files.Shares.Models.ShareProperties ShareProperties(string accessTier = null, System.DateTimeOffset? lastModified = default(System.DateTimeOffset?), int? provisionedIops = default(int?), int? provisionedIngressMBps = default(int?), int? provisionedEgressMBps = default(int?), System.DateTimeOffset? nextAllowedQuotaDowngradeTime = default(System.DateTimeOffset?), System.DateTimeOffset? deletedOn = default(System.DateTimeOffset?), int? remainingRetentionDays = default(int?), Azure.ETag? eTag = default(Azure.ETag?), System.DateTimeOffset? accessTierChangeTime = default(System.DateTimeOffset?), string accessTierTransitionState = null, Azure.Storage.Files.Shares.Models.ShareLeaseStatus? leaseStatus = default(Azure.Storage.Files.Shares.Models.ShareLeaseStatus?), Azure.Storage.Files.Shares.Models.ShareLeaseState? leaseState = default(Azure.Storage.Files.Shares.Models.ShareLeaseState?), Azure.Storage.Files.Shares.Models.ShareLeaseDuration? leaseDuration = default(Azure.Storage.Files.Shares.Models.ShareLeaseDuration?), int? quotaInGB = default(int?), System.Collections.Generic.IDictionary metadata = null, Azure.Storage.Files.Shares.Models.ShareProtocols? protocols = default(Azure.Storage.Files.Shares.Models.ShareProtocols?), Azure.Storage.Files.Shares.Models.ShareRootSquash? rootSquash = default(Azure.Storage.Files.Shares.Models.ShareRootSquash?), bool? enableSnapshotVirtualDirectoryAccess = default(bool?), bool? enablePaidBursting = default(bool?), long? paidBurstingMaxIops = default(long?), long? paidBustingMaxBandwidthMibps = default(long?)) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public static Azure.Storage.Files.Shares.Models.ShareProperties ShareProperties(string accessTier, System.DateTimeOffset? lastModified, int? provisionedIops, int? provisionedIngressMBps, int? provisionedEgressMBps, System.DateTimeOffset? nextAllowedQuotaDowngradeTime, System.DateTimeOffset? deletedOn, int? remainingRetentionDays, Azure.ETag? eTag, System.DateTimeOffset? accessTierChangeTime, string accessTierTransitionState, Azure.Storage.Files.Shares.Models.ShareLeaseStatus? leaseStatus, Azure.Storage.Files.Shares.Models.ShareLeaseState? leaseState, Azure.Storage.Files.Shares.Models.ShareLeaseDuration? leaseDuration, int? quotaInGB, System.Collections.Generic.IDictionary metadata, Azure.Storage.Files.Shares.Models.ShareProtocols? protocols, Azure.Storage.Files.Shares.Models.ShareRootSquash? rootSquash, bool? enableSnapshotVirtualDirectoryAccess, bool? enablePaidBursting, long? paidBurstingMaxIops, long? paidBustingMaxBandwidthMibps) { throw null; } + public static Azure.Storage.Files.Shares.Models.ShareProperties ShareProperties(string accessTier = null, System.DateTimeOffset? lastModified = default(System.DateTimeOffset?), int? provisionedIops = default(int?), int? provisionedIngressMBps = default(int?), int? provisionedEgressMBps = default(int?), System.DateTimeOffset? nextAllowedQuotaDowngradeTime = default(System.DateTimeOffset?), System.DateTimeOffset? deletedOn = default(System.DateTimeOffset?), int? remainingRetentionDays = default(int?), Azure.ETag? eTag = default(Azure.ETag?), System.DateTimeOffset? accessTierChangeTime = default(System.DateTimeOffset?), string accessTierTransitionState = null, Azure.Storage.Files.Shares.Models.ShareLeaseStatus? leaseStatus = default(Azure.Storage.Files.Shares.Models.ShareLeaseStatus?), Azure.Storage.Files.Shares.Models.ShareLeaseState? leaseState = default(Azure.Storage.Files.Shares.Models.ShareLeaseState?), Azure.Storage.Files.Shares.Models.ShareLeaseDuration? leaseDuration = default(Azure.Storage.Files.Shares.Models.ShareLeaseDuration?), int? quotaInGB = default(int?), System.Collections.Generic.IDictionary metadata = null, Azure.Storage.Files.Shares.Models.ShareProtocols? protocols = default(Azure.Storage.Files.Shares.Models.ShareProtocols?), Azure.Storage.Files.Shares.Models.ShareRootSquash? rootSquash = default(Azure.Storage.Files.Shares.Models.ShareRootSquash?), bool? enableSnapshotVirtualDirectoryAccess = default(bool?), bool? enablePaidBursting = default(bool?), long? paidBurstingMaxIops = default(long?), long? paidBustingMaxBandwidthMibps = default(long?), long? includedBurstIops = default(long?), long? maxBurstCreditsForIops = default(long?), System.DateTimeOffset? nextAllowedProvisionedIopsDowngradeTime = default(System.DateTimeOffset?), System.DateTimeOffset? nextAllowedProvisionedBandwidthDowngradeTime = default(System.DateTimeOffset?)) { throw null; } public static Azure.Storage.Files.Shares.Models.ShareSnapshotInfo ShareSnapshotInfo(string snapshot, Azure.ETag eTag, System.DateTimeOffset lastModified) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public static Azure.Storage.Files.Shares.Models.ShareStatistics ShareStatistics(int shareUsageBytes) { throw null; } @@ -1134,11 +1144,15 @@ internal ShareProperties() { } public bool? EnablePaidBursting { get { throw null; } } public bool? EnableSnapshotVirtualDirectoryAccess { get { throw null; } } public Azure.ETag? ETag { get { throw null; } } + public long? IncludedBurstIops { get { throw null; } } public System.DateTimeOffset? LastModified { get { throw null; } } public Azure.Storage.Files.Shares.Models.ShareLeaseDuration? LeaseDuration { get { throw null; } } public Azure.Storage.Files.Shares.Models.ShareLeaseState? LeaseState { get { throw null; } } public Azure.Storage.Files.Shares.Models.ShareLeaseStatus? LeaseStatus { get { throw null; } } + public long? MaxBurstCreditsForIops { get { throw null; } } public System.Collections.Generic.IDictionary Metadata { get { throw null; } } + public System.DateTimeOffset? NextAllowedProvisionedBandwidthDowngradeTime { get { throw null; } } + public System.DateTimeOffset? NextAllowedProvisionedIopsDowngradeTime { get { throw null; } } public System.DateTimeOffset? NextAllowedQuotaDowngradeTime { get { throw null; } } public long? PaidBurstingMaxBandwidthMibps { get { throw null; } } public long? PaidBurstingMaxIops { get { throw null; } } @@ -1193,6 +1207,8 @@ public ShareSetPropertiesOptions() { } public bool? EnableSnapshotVirtualDirectoryAccess { get { throw null; } set { } } public long? PaidBurstingMaxBandwidthMibps { get { throw null; } set { } } public long? PaidBurstingMaxIops { get { throw null; } set { } } + public long? ProvisionedMaxBandwidthMibps { get { throw null; } set { } } + public long? ProvisionedMaxIops { get { throw null; } set { } } public int? QuotaInGB { get { throw null; } set { } } public Azure.Storage.Files.Shares.Models.ShareRootSquash? RootSquash { get { throw null; } set { } } } diff --git a/sdk/storage/Azure.Storage.Files.Shares/api/Azure.Storage.Files.Shares.netstandard2.0.cs b/sdk/storage/Azure.Storage.Files.Shares/api/Azure.Storage.Files.Shares.netstandard2.0.cs index 4c5f277eb6166..b1b355dda471c 100644 --- a/sdk/storage/Azure.Storage.Files.Shares/api/Azure.Storage.Files.Shares.netstandard2.0.cs +++ b/sdk/storage/Azure.Storage.Files.Shares/api/Azure.Storage.Files.Shares.netstandard2.0.cs @@ -25,11 +25,12 @@ public ShareClient(System.Uri shareUri, Azure.Storage.StorageSharedKeyCredential public virtual System.Threading.Tasks.Task> CreateDirectoryAsync(string directoryName, Azure.Storage.Files.Shares.Models.ShareDirectoryCreateOptions options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public virtual System.Threading.Tasks.Task> CreateDirectoryAsync(string directoryName, System.Collections.Generic.IDictionary metadata, Azure.Storage.Files.Shares.Models.FileSmbProperties smbProperties, string filePermission, System.Threading.CancellationToken cancellationToken) { throw null; } - public virtual Azure.Response CreateIfNotExists(Azure.Storage.Files.Shares.Models.ShareCreateOptions options, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual Azure.Response CreateIfNotExists(Azure.Storage.Files.Shares.Models.ShareCreateOptions options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] - public virtual Azure.Response CreateIfNotExists(System.Collections.Generic.IDictionary metadata = null, int? quotaInGB = default(int?), System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } - public virtual System.Threading.Tasks.Task> CreateIfNotExistsAsync(Azure.Storage.Files.Shares.Models.ShareCreateOptions options, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } - public virtual System.Threading.Tasks.Task> CreateIfNotExistsAsync(System.Collections.Generic.IDictionary metadata = null, int? quotaInGB = default(int?), System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual Azure.Response CreateIfNotExists(System.Collections.Generic.IDictionary metadata, int? quotaInGB, System.Threading.CancellationToken cancellationToken) { throw null; } + public virtual System.Threading.Tasks.Task> CreateIfNotExistsAsync(Azure.Storage.Files.Shares.Models.ShareCreateOptions options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public virtual System.Threading.Tasks.Task> CreateIfNotExistsAsync(System.Collections.Generic.IDictionary metadata, int? quotaInGB, System.Threading.CancellationToken cancellationToken) { throw null; } public virtual Azure.Response CreatePermission(Azure.Storage.Files.Shares.Models.ShareFilePermission permission, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public virtual Azure.Response CreatePermission(string permission, System.Threading.CancellationToken cancellationToken) { throw null; } @@ -114,7 +115,7 @@ public ShareClient(System.Uri shareUri, Azure.Storage.StorageSharedKeyCredential } public partial class ShareClientOptions : Azure.Core.ClientOptions { - public ShareClientOptions(Azure.Storage.Files.Shares.ShareClientOptions.ServiceVersion version = Azure.Storage.Files.Shares.ShareClientOptions.ServiceVersion.V2024_11_04) { } + public ShareClientOptions(Azure.Storage.Files.Shares.ShareClientOptions.ServiceVersion version = Azure.Storage.Files.Shares.ShareClientOptions.ServiceVersion.V2025_01_05) { } public bool? AllowSourceTrailingDot { get { throw null; } set { } } public bool? AllowTrailingDot { get { throw null; } set { } } public Azure.Storage.Files.Shares.Models.ShareAudience? Audience { get { throw null; } set { } } @@ -147,6 +148,7 @@ public enum ServiceVersion V2024_05_04 = 22, V2024_08_04 = 23, V2024_11_04 = 24, + V2025_01_05 = 25, } } public partial class ShareDirectoryClient @@ -562,6 +564,7 @@ public ShareAccessPolicy() { } public ShareAccessTier(string value) { throw null; } public static Azure.Storage.Files.Shares.Models.ShareAccessTier Cool { get { throw null; } } public static Azure.Storage.Files.Shares.Models.ShareAccessTier Hot { get { throw null; } } + public static Azure.Storage.Files.Shares.Models.ShareAccessTier Premium { get { throw null; } } public static Azure.Storage.Files.Shares.Models.ShareAccessTier TransactionOptimized { get { throw null; } } public bool Equals(Azure.Storage.Files.Shares.Models.ShareAccessTier other) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] @@ -610,6 +613,8 @@ public ShareCreateOptions() { } public long? PaidBurstingMaxBandwidthMibps { get { throw null; } set { } } public long? PaidBurstingMaxIops { get { throw null; } set { } } public Azure.Storage.Files.Shares.Models.ShareProtocols? Protocols { get { throw null; } set { } } + public long? ProvisionedMaxBandwidthMibps { get { throw null; } set { } } + public long? ProvisionedMaxIops { get { throw null; } set { } } public int? QuotaInGB { get { throw null; } set { } } public Azure.Storage.Files.Shares.Models.ShareRootSquash? RootSquash { get { throw null; } set { } } } @@ -681,6 +686,8 @@ public ShareDirectorySetHttpHeadersOptions() { } public static Azure.Storage.Files.Shares.Models.ShareErrorCode EmptyMetadataKey { get { throw null; } } public static Azure.Storage.Files.Shares.Models.ShareErrorCode FeatureVersionMismatch { get { throw null; } } public static Azure.Storage.Files.Shares.Models.ShareErrorCode FileLockConflict { get { throw null; } } + public static Azure.Storage.Files.Shares.Models.ShareErrorCode FileShareProvisionedBandwidthDowngradeNotAllowed { get { throw null; } } + public static Azure.Storage.Files.Shares.Models.ShareErrorCode FileShareProvisionedIopsDowngradeNotAllowed { get { throw null; } } public static Azure.Storage.Files.Shares.Models.ShareErrorCode InsufficientAccountPermissions { get { throw null; } } public static Azure.Storage.Files.Shares.Models.ShareErrorCode InternalError { get { throw null; } } public static Azure.Storage.Files.Shares.Models.ShareErrorCode InvalidAuthenticationInfo { get { throw null; } } @@ -760,6 +767,7 @@ public ShareFileCopyOptions() { } public Azure.Storage.Files.Shares.Models.PermissionCopyMode? FilePermissionCopyMode { get { throw null; } set { } } public bool? IgnoreReadOnly { get { throw null; } set { } } public System.Collections.Generic.IDictionary Metadata { get { throw null; } set { } } + public Azure.Storage.Files.Shares.Models.FilePermissionFormat? PermissionFormat { get { throw null; } set { } } public Azure.Storage.Files.Shares.Models.FileSmbProperties SmbProperties { get { throw null; } set { } } public Azure.Storage.Files.Shares.Models.CopyableFileSmbProperties SmbPropertiesToCopy { get { throw null; } set { } } } @@ -1115,7 +1123,9 @@ public static partial class ShareModelFactory public static Azure.Storage.Files.Shares.Models.ShareProperties ShareProperties(string accessTier = null, System.DateTimeOffset? lastModified = default(System.DateTimeOffset?), int? provisionedIops = default(int?), int? provisionedIngressMBps = default(int?), int? provisionedEgressMBps = default(int?), System.DateTimeOffset? nextAllowedQuotaDowngradeTime = default(System.DateTimeOffset?), System.DateTimeOffset? deletedOn = default(System.DateTimeOffset?), int? remainingRetentionDays = default(int?), Azure.ETag? eTag = default(Azure.ETag?), System.DateTimeOffset? accessTierChangeTime = default(System.DateTimeOffset?), string accessTierTransitionState = null, Azure.Storage.Files.Shares.Models.ShareLeaseStatus? leaseStatus = default(Azure.Storage.Files.Shares.Models.ShareLeaseStatus?), Azure.Storage.Files.Shares.Models.ShareLeaseState? leaseState = default(Azure.Storage.Files.Shares.Models.ShareLeaseState?), Azure.Storage.Files.Shares.Models.ShareLeaseDuration? leaseDuration = default(Azure.Storage.Files.Shares.Models.ShareLeaseDuration?), int? quotaInGB = default(int?), System.Collections.Generic.IDictionary metadata = null, Azure.Storage.Files.Shares.Models.ShareProtocols? protocols = default(Azure.Storage.Files.Shares.Models.ShareProtocols?), Azure.Storage.Files.Shares.Models.ShareRootSquash? rootSquash = default(Azure.Storage.Files.Shares.Models.ShareRootSquash?)) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public static Azure.Storage.Files.Shares.Models.ShareProperties ShareProperties(string accessTier, System.DateTimeOffset? lastModified, int? provisionedIops, int? provisionedIngressMBps, int? provisionedEgressMBps, System.DateTimeOffset? nextAllowedQuotaDowngradeTime, System.DateTimeOffset? deletedOn, int? remainingRetentionDays, Azure.ETag? eTag, System.DateTimeOffset? accessTierChangeTime, string accessTierTransitionState, Azure.Storage.Files.Shares.Models.ShareLeaseStatus? leaseStatus, Azure.Storage.Files.Shares.Models.ShareLeaseState? leaseState, Azure.Storage.Files.Shares.Models.ShareLeaseDuration? leaseDuration, int? quotaInGB, System.Collections.Generic.IDictionary metadata, Azure.Storage.Files.Shares.Models.ShareProtocols? protocols, Azure.Storage.Files.Shares.Models.ShareRootSquash? rootSquash, bool? enableSnapshotVirtualDirectoryAccess) { throw null; } - public static Azure.Storage.Files.Shares.Models.ShareProperties ShareProperties(string accessTier = null, System.DateTimeOffset? lastModified = default(System.DateTimeOffset?), int? provisionedIops = default(int?), int? provisionedIngressMBps = default(int?), int? provisionedEgressMBps = default(int?), System.DateTimeOffset? nextAllowedQuotaDowngradeTime = default(System.DateTimeOffset?), System.DateTimeOffset? deletedOn = default(System.DateTimeOffset?), int? remainingRetentionDays = default(int?), Azure.ETag? eTag = default(Azure.ETag?), System.DateTimeOffset? accessTierChangeTime = default(System.DateTimeOffset?), string accessTierTransitionState = null, Azure.Storage.Files.Shares.Models.ShareLeaseStatus? leaseStatus = default(Azure.Storage.Files.Shares.Models.ShareLeaseStatus?), Azure.Storage.Files.Shares.Models.ShareLeaseState? leaseState = default(Azure.Storage.Files.Shares.Models.ShareLeaseState?), Azure.Storage.Files.Shares.Models.ShareLeaseDuration? leaseDuration = default(Azure.Storage.Files.Shares.Models.ShareLeaseDuration?), int? quotaInGB = default(int?), System.Collections.Generic.IDictionary metadata = null, Azure.Storage.Files.Shares.Models.ShareProtocols? protocols = default(Azure.Storage.Files.Shares.Models.ShareProtocols?), Azure.Storage.Files.Shares.Models.ShareRootSquash? rootSquash = default(Azure.Storage.Files.Shares.Models.ShareRootSquash?), bool? enableSnapshotVirtualDirectoryAccess = default(bool?), bool? enablePaidBursting = default(bool?), long? paidBurstingMaxIops = default(long?), long? paidBustingMaxBandwidthMibps = default(long?)) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public static Azure.Storage.Files.Shares.Models.ShareProperties ShareProperties(string accessTier, System.DateTimeOffset? lastModified, int? provisionedIops, int? provisionedIngressMBps, int? provisionedEgressMBps, System.DateTimeOffset? nextAllowedQuotaDowngradeTime, System.DateTimeOffset? deletedOn, int? remainingRetentionDays, Azure.ETag? eTag, System.DateTimeOffset? accessTierChangeTime, string accessTierTransitionState, Azure.Storage.Files.Shares.Models.ShareLeaseStatus? leaseStatus, Azure.Storage.Files.Shares.Models.ShareLeaseState? leaseState, Azure.Storage.Files.Shares.Models.ShareLeaseDuration? leaseDuration, int? quotaInGB, System.Collections.Generic.IDictionary metadata, Azure.Storage.Files.Shares.Models.ShareProtocols? protocols, Azure.Storage.Files.Shares.Models.ShareRootSquash? rootSquash, bool? enableSnapshotVirtualDirectoryAccess, bool? enablePaidBursting, long? paidBurstingMaxIops, long? paidBustingMaxBandwidthMibps) { throw null; } + public static Azure.Storage.Files.Shares.Models.ShareProperties ShareProperties(string accessTier = null, System.DateTimeOffset? lastModified = default(System.DateTimeOffset?), int? provisionedIops = default(int?), int? provisionedIngressMBps = default(int?), int? provisionedEgressMBps = default(int?), System.DateTimeOffset? nextAllowedQuotaDowngradeTime = default(System.DateTimeOffset?), System.DateTimeOffset? deletedOn = default(System.DateTimeOffset?), int? remainingRetentionDays = default(int?), Azure.ETag? eTag = default(Azure.ETag?), System.DateTimeOffset? accessTierChangeTime = default(System.DateTimeOffset?), string accessTierTransitionState = null, Azure.Storage.Files.Shares.Models.ShareLeaseStatus? leaseStatus = default(Azure.Storage.Files.Shares.Models.ShareLeaseStatus?), Azure.Storage.Files.Shares.Models.ShareLeaseState? leaseState = default(Azure.Storage.Files.Shares.Models.ShareLeaseState?), Azure.Storage.Files.Shares.Models.ShareLeaseDuration? leaseDuration = default(Azure.Storage.Files.Shares.Models.ShareLeaseDuration?), int? quotaInGB = default(int?), System.Collections.Generic.IDictionary metadata = null, Azure.Storage.Files.Shares.Models.ShareProtocols? protocols = default(Azure.Storage.Files.Shares.Models.ShareProtocols?), Azure.Storage.Files.Shares.Models.ShareRootSquash? rootSquash = default(Azure.Storage.Files.Shares.Models.ShareRootSquash?), bool? enableSnapshotVirtualDirectoryAccess = default(bool?), bool? enablePaidBursting = default(bool?), long? paidBurstingMaxIops = default(long?), long? paidBustingMaxBandwidthMibps = default(long?), long? includedBurstIops = default(long?), long? maxBurstCreditsForIops = default(long?), System.DateTimeOffset? nextAllowedProvisionedIopsDowngradeTime = default(System.DateTimeOffset?), System.DateTimeOffset? nextAllowedProvisionedBandwidthDowngradeTime = default(System.DateTimeOffset?)) { throw null; } public static Azure.Storage.Files.Shares.Models.ShareSnapshotInfo ShareSnapshotInfo(string snapshot, Azure.ETag eTag, System.DateTimeOffset lastModified) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public static Azure.Storage.Files.Shares.Models.ShareStatistics ShareStatistics(int shareUsageBytes) { throw null; } @@ -1134,11 +1144,15 @@ internal ShareProperties() { } public bool? EnablePaidBursting { get { throw null; } } public bool? EnableSnapshotVirtualDirectoryAccess { get { throw null; } } public Azure.ETag? ETag { get { throw null; } } + public long? IncludedBurstIops { get { throw null; } } public System.DateTimeOffset? LastModified { get { throw null; } } public Azure.Storage.Files.Shares.Models.ShareLeaseDuration? LeaseDuration { get { throw null; } } public Azure.Storage.Files.Shares.Models.ShareLeaseState? LeaseState { get { throw null; } } public Azure.Storage.Files.Shares.Models.ShareLeaseStatus? LeaseStatus { get { throw null; } } + public long? MaxBurstCreditsForIops { get { throw null; } } public System.Collections.Generic.IDictionary Metadata { get { throw null; } } + public System.DateTimeOffset? NextAllowedProvisionedBandwidthDowngradeTime { get { throw null; } } + public System.DateTimeOffset? NextAllowedProvisionedIopsDowngradeTime { get { throw null; } } public System.DateTimeOffset? NextAllowedQuotaDowngradeTime { get { throw null; } } public long? PaidBurstingMaxBandwidthMibps { get { throw null; } } public long? PaidBurstingMaxIops { get { throw null; } } @@ -1193,6 +1207,8 @@ public ShareSetPropertiesOptions() { } public bool? EnableSnapshotVirtualDirectoryAccess { get { throw null; } set { } } public long? PaidBurstingMaxBandwidthMibps { get { throw null; } set { } } public long? PaidBurstingMaxIops { get { throw null; } set { } } + public long? ProvisionedMaxBandwidthMibps { get { throw null; } set { } } + public long? ProvisionedMaxIops { get { throw null; } set { } } public int? QuotaInGB { get { throw null; } set { } } public Azure.Storage.Files.Shares.Models.ShareRootSquash? RootSquash { get { throw null; } set { } } } diff --git a/sdk/storage/Azure.Storage.Files.Shares/assets.json b/sdk/storage/Azure.Storage.Files.Shares/assets.json index 43af8c5e92a84..c2b5c3d31e6a2 100644 --- a/sdk/storage/Azure.Storage.Files.Shares/assets.json +++ b/sdk/storage/Azure.Storage.Files.Shares/assets.json @@ -2,5 +2,5 @@ "AssetsRepo": "Azure/azure-sdk-assets", "AssetsRepoPrefixPath": "net", "TagPrefix": "net/storage/Azure.Storage.Files.Shares", - "Tag": "net/storage/Azure.Storage.Files.Shares_772bdf674f" + "Tag": "net/storage/Azure.Storage.Files.Shares_df67d82d59" } diff --git a/sdk/storage/Azure.Storage.Files.Shares/src/Generated/DirectoryRestClient.cs b/sdk/storage/Azure.Storage.Files.Shares/src/Generated/DirectoryRestClient.cs index 961c6ff47ce59..8a2edb8b99134 100644 --- a/sdk/storage/Azure.Storage.Files.Shares/src/Generated/DirectoryRestClient.cs +++ b/sdk/storage/Azure.Storage.Files.Shares/src/Generated/DirectoryRestClient.cs @@ -33,7 +33,7 @@ internal partial class DirectoryRestClient /// The handler for diagnostic messaging in the client. /// The HTTP pipeline for sending and receiving REST requests and responses. /// The URL of the service account, share, directory or file that is the target of the desired operation. - /// Specifies the version of the operation to use for this request. The default value is "2024-11-04". + /// Specifies the version of the operation to use for this request. The default value is "2025-01-05". /// If true, the trailing dot will not be trimmed from the target URI. /// Valid value is backup. /// If true, the trailing dot will not be trimmed from the source URI. diff --git a/sdk/storage/Azure.Storage.Files.Shares/src/Generated/FileDownloadHeaders.cs b/sdk/storage/Azure.Storage.Files.Shares/src/Generated/FileDownloadHeaders.cs index 61384dee810d4..c4d7056a5cfa3 100644 --- a/sdk/storage/Azure.Storage.Files.Shares/src/Generated/FileDownloadHeaders.cs +++ b/sdk/storage/Azure.Storage.Files.Shares/src/Generated/FileDownloadHeaders.cs @@ -79,5 +79,9 @@ public FileDownloadHeaders(Response response) public ShareLeaseState? LeaseState => _response.Headers.TryGetValue("x-ms-lease-state", out string value) ? value.ToShareLeaseState() : null; /// The current lease status of the file. public ShareLeaseStatus? LeaseStatus => _response.Headers.TryGetValue("x-ms-lease-status", out string value) ? value.ToShareLeaseStatus() : null; + /// Indicates the response body contains a structured message and specifies the message schema version and properties. + public string StructuredBodyType => _response.Headers.TryGetValue("x-ms-structured-body", out string value) ? value : null; + /// The length of the blob/file content inside the message body when the response body is returned as a structured message. Will always be smaller than Content-Length. + public long? StructuredContentLength => _response.Headers.TryGetValue("x-ms-structured-content-length", out long? value) ? value : null; } } diff --git a/sdk/storage/Azure.Storage.Files.Shares/src/Generated/FileRestClient.cs b/sdk/storage/Azure.Storage.Files.Shares/src/Generated/FileRestClient.cs index d4b584e6660ee..07f88af545aec 100644 --- a/sdk/storage/Azure.Storage.Files.Shares/src/Generated/FileRestClient.cs +++ b/sdk/storage/Azure.Storage.Files.Shares/src/Generated/FileRestClient.cs @@ -34,7 +34,7 @@ internal partial class FileRestClient /// The handler for diagnostic messaging in the client. /// The HTTP pipeline for sending and receiving REST requests and responses. /// The URL of the service account, share, directory or file that is the target of the desired operation. - /// Specifies the version of the operation to use for this request. The default value is "2024-11-04". + /// Specifies the version of the operation to use for this request. The default value is "2025-01-05". /// Only update is supported: - Update: Writes the bytes downloaded from the source url into the specified range. The default value is "update". /// If true, the trailing dot will not be trimmed from the target URI. /// Valid value is backup. @@ -204,7 +204,7 @@ public ResponseWithHeaders Create(long fileContentLength, str } } - internal HttpMessage CreateDownloadRequest(int? timeout, string range, bool? rangeGetContentMD5, ShareFileRequestConditions shareFileRequestConditions) + internal HttpMessage CreateDownloadRequest(int? timeout, string range, bool? rangeGetContentMD5, string structuredBodyType, ShareFileRequestConditions shareFileRequestConditions) { var message = _pipeline.CreateMessage(); var request = message.Request; @@ -230,6 +230,10 @@ internal HttpMessage CreateDownloadRequest(int? timeout, string range, bool? ran { request.Headers.Add("x-ms-range-get-content-md5", rangeGetContentMD5.Value); } + if (structuredBodyType != null) + { + request.Headers.Add("x-ms-structured-body", structuredBodyType); + } if (shareFileRequestConditions?.LeaseId != null) { request.Headers.Add("x-ms-lease-id", shareFileRequestConditions.LeaseId); @@ -246,11 +250,12 @@ internal HttpMessage CreateDownloadRequest(int? timeout, string range, bool? ran /// The timeout parameter is expressed in seconds. For more information, see <a href="https://docs.microsoft.com/en-us/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations?redirectedfrom=MSDN">Setting Timeouts for File Service Operations.</a>. /// Return file data only from the specified byte range. /// When this header is set to true and specified together with the Range header, the service returns the MD5 hash for the range, as long as the range is less than or equal to 4 MB in size. + /// Specifies the response content should be returned as a structured message and specifies the message schema version and properties. /// Parameter group. /// The cancellation token to use. - public async Task> DownloadAsync(int? timeout = null, string range = null, bool? rangeGetContentMD5 = null, ShareFileRequestConditions shareFileRequestConditions = null, CancellationToken cancellationToken = default) + public async Task> DownloadAsync(int? timeout = null, string range = null, bool? rangeGetContentMD5 = null, string structuredBodyType = null, ShareFileRequestConditions shareFileRequestConditions = null, CancellationToken cancellationToken = default) { - using var message = CreateDownloadRequest(timeout, range, rangeGetContentMD5, shareFileRequestConditions); + using var message = CreateDownloadRequest(timeout, range, rangeGetContentMD5, structuredBodyType, shareFileRequestConditions); await _pipeline.SendAsync(message, cancellationToken).ConfigureAwait(false); var headers = new FileDownloadHeaders(message.Response); switch (message.Response.Status) @@ -270,11 +275,12 @@ public async Task> DownloadAsyn /// The timeout parameter is expressed in seconds. For more information, see <a href="https://docs.microsoft.com/en-us/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations?redirectedfrom=MSDN">Setting Timeouts for File Service Operations.</a>. /// Return file data only from the specified byte range. /// When this header is set to true and specified together with the Range header, the service returns the MD5 hash for the range, as long as the range is less than or equal to 4 MB in size. + /// Specifies the response content should be returned as a structured message and specifies the message schema version and properties. /// Parameter group. /// The cancellation token to use. - public ResponseWithHeaders Download(int? timeout = null, string range = null, bool? rangeGetContentMD5 = null, ShareFileRequestConditions shareFileRequestConditions = null, CancellationToken cancellationToken = default) + public ResponseWithHeaders Download(int? timeout = null, string range = null, bool? rangeGetContentMD5 = null, string structuredBodyType = null, ShareFileRequestConditions shareFileRequestConditions = null, CancellationToken cancellationToken = default) { - using var message = CreateDownloadRequest(timeout, range, rangeGetContentMD5, shareFileRequestConditions); + using var message = CreateDownloadRequest(timeout, range, rangeGetContentMD5, structuredBodyType, shareFileRequestConditions); _pipeline.Send(message, cancellationToken); var headers = new FileDownloadHeaders(message.Response); switch (message.Response.Status) @@ -945,7 +951,7 @@ public ResponseWithHeaders BreakLease(int? timeout = null } } - internal HttpMessage CreateUploadRangeRequest(string range, ShareFileRangeWriteType fileRangeWrite, long contentLength, int? timeout, byte[] contentMD5, FileLastWrittenMode? fileLastWrittenMode, Stream optionalbody, ShareFileRequestConditions shareFileRequestConditions) + internal HttpMessage CreateUploadRangeRequest(string range, ShareFileRangeWriteType fileRangeWrite, long contentLength, int? timeout, byte[] contentMD5, FileLastWrittenMode? fileLastWrittenMode, string structuredBodyType, long? structuredContentLength, Stream optionalbody, ShareFileRequestConditions shareFileRequestConditions) { var message = _pipeline.CreateMessage(); var request = message.Request; @@ -977,6 +983,14 @@ internal HttpMessage CreateUploadRangeRequest(string range, ShareFileRangeWriteT { request.Headers.Add("x-ms-file-request-intent", _fileRequestIntent.Value.ToString()); } + if (structuredBodyType != null) + { + request.Headers.Add("x-ms-structured-body", structuredBodyType); + } + if (structuredContentLength != null) + { + request.Headers.Add("x-ms-structured-content-length", structuredContentLength.Value); + } request.Headers.Add("Accept", "application/xml"); if (optionalbody != null) { @@ -998,18 +1012,20 @@ internal HttpMessage CreateUploadRangeRequest(string range, ShareFileRangeWriteT /// The timeout parameter is expressed in seconds. For more information, see <a href="https://docs.microsoft.com/en-us/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations?redirectedfrom=MSDN">Setting Timeouts for File Service Operations.</a>. /// An MD5 hash of the content. This hash is used to verify the integrity of the data during transport. When the Content-MD5 header is specified, the File service compares the hash of the content that has arrived with the header value that was sent. If the two hashes do not match, the operation will fail with error code 400 (Bad Request). /// If the file last write time should be preserved or overwritten. + /// Required if the request body is a structured message. Specifies the message schema version and properties. + /// Required if the request body is a structured message. Specifies the length of the blob/file content inside the message body. Will always be smaller than Content-Length. /// Initial data. /// Parameter group. /// The cancellation token to use. /// is null. - public async Task> UploadRangeAsync(string range, ShareFileRangeWriteType fileRangeWrite, long contentLength, int? timeout = null, byte[] contentMD5 = null, FileLastWrittenMode? fileLastWrittenMode = null, Stream optionalbody = null, ShareFileRequestConditions shareFileRequestConditions = null, CancellationToken cancellationToken = default) + public async Task> UploadRangeAsync(string range, ShareFileRangeWriteType fileRangeWrite, long contentLength, int? timeout = null, byte[] contentMD5 = null, FileLastWrittenMode? fileLastWrittenMode = null, string structuredBodyType = null, long? structuredContentLength = null, Stream optionalbody = null, ShareFileRequestConditions shareFileRequestConditions = null, CancellationToken cancellationToken = default) { if (range == null) { throw new ArgumentNullException(nameof(range)); } - using var message = CreateUploadRangeRequest(range, fileRangeWrite, contentLength, timeout, contentMD5, fileLastWrittenMode, optionalbody, shareFileRequestConditions); + using var message = CreateUploadRangeRequest(range, fileRangeWrite, contentLength, timeout, contentMD5, fileLastWrittenMode, structuredBodyType, structuredContentLength, optionalbody, shareFileRequestConditions); await _pipeline.SendAsync(message, cancellationToken).ConfigureAwait(false); var headers = new FileUploadRangeHeaders(message.Response); switch (message.Response.Status) @@ -1028,18 +1044,20 @@ public async Task> UploadRangeAsync( /// The timeout parameter is expressed in seconds. For more information, see <a href="https://docs.microsoft.com/en-us/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations?redirectedfrom=MSDN">Setting Timeouts for File Service Operations.</a>. /// An MD5 hash of the content. This hash is used to verify the integrity of the data during transport. When the Content-MD5 header is specified, the File service compares the hash of the content that has arrived with the header value that was sent. If the two hashes do not match, the operation will fail with error code 400 (Bad Request). /// If the file last write time should be preserved or overwritten. + /// Required if the request body is a structured message. Specifies the message schema version and properties. + /// Required if the request body is a structured message. Specifies the length of the blob/file content inside the message body. Will always be smaller than Content-Length. /// Initial data. /// Parameter group. /// The cancellation token to use. /// is null. - public ResponseWithHeaders UploadRange(string range, ShareFileRangeWriteType fileRangeWrite, long contentLength, int? timeout = null, byte[] contentMD5 = null, FileLastWrittenMode? fileLastWrittenMode = null, Stream optionalbody = null, ShareFileRequestConditions shareFileRequestConditions = null, CancellationToken cancellationToken = default) + public ResponseWithHeaders UploadRange(string range, ShareFileRangeWriteType fileRangeWrite, long contentLength, int? timeout = null, byte[] contentMD5 = null, FileLastWrittenMode? fileLastWrittenMode = null, string structuredBodyType = null, long? structuredContentLength = null, Stream optionalbody = null, ShareFileRequestConditions shareFileRequestConditions = null, CancellationToken cancellationToken = default) { if (range == null) { throw new ArgumentNullException(nameof(range)); } - using var message = CreateUploadRangeRequest(range, fileRangeWrite, contentLength, timeout, contentMD5, fileLastWrittenMode, optionalbody, shareFileRequestConditions); + using var message = CreateUploadRangeRequest(range, fileRangeWrite, contentLength, timeout, contentMD5, fileLastWrittenMode, structuredBodyType, structuredContentLength, optionalbody, shareFileRequestConditions); _pipeline.Send(message, cancellationToken); var headers = new FileUploadRangeHeaders(message.Response); switch (message.Response.Status) @@ -1290,7 +1308,7 @@ public ResponseWithHeaders GetRange } } - internal HttpMessage CreateStartCopyRequest(string copySource, int? timeout, IDictionary metadata, string filePermission, string filePermissionKey, CopyFileSmbInfo copyFileSmbInfo, ShareFileRequestConditions shareFileRequestConditions) + internal HttpMessage CreateStartCopyRequest(string copySource, int? timeout, IDictionary metadata, string filePermission, FilePermissionFormat? filePermissionFormat, string filePermissionKey, CopyFileSmbInfo copyFileSmbInfo, ShareFileRequestConditions shareFileRequestConditions) { var message = _pipeline.CreateMessage(); var request = message.Request; @@ -1312,6 +1330,10 @@ internal HttpMessage CreateStartCopyRequest(string copySource, int? timeout, IDi { request.Headers.Add("x-ms-file-permission", filePermission); } + if (filePermissionFormat != null) + { + request.Headers.Add("x-ms-file-permission-format", filePermissionFormat.Value.ToSerialString()); + } if (filePermissionKey != null) { request.Headers.Add("x-ms-file-permission-key", filePermissionKey); @@ -1369,19 +1391,20 @@ internal HttpMessage CreateStartCopyRequest(string copySource, int? timeout, IDi /// The timeout parameter is expressed in seconds. For more information, see <a href="https://docs.microsoft.com/en-us/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations?redirectedfrom=MSDN">Setting Timeouts for File Service Operations.</a>. /// A name-value pair to associate with a file storage object. /// If specified the permission (security descriptor) shall be set for the directory/file. This header can be used if Permission size is <= 8KB, else x-ms-file-permission-key header shall be used. Default value: Inherit. If SDDL is specified as input, it must have owner, group and dacl. Note: Only one of the x-ms-file-permission or x-ms-file-permission-key should be specified. + /// Optional. Available for version 2023-06-01 and later. Specifies the format in which the permission is returned. Acceptable values are SDDL or binary. If x-ms-file-permission-format is unspecified or explicitly set to SDDL, the permission is returned in SDDL format. If x-ms-file-permission-format is explicitly set to binary, the permission is returned as a base64 string representing the binary encoding of the permission. /// Key of the permission to be set for the directory/file. Note: Only one of the x-ms-file-permission or x-ms-file-permission-key should be specified. /// Parameter group. /// Parameter group. /// The cancellation token to use. /// is null. - public async Task> StartCopyAsync(string copySource, int? timeout = null, IDictionary metadata = null, string filePermission = null, string filePermissionKey = null, CopyFileSmbInfo copyFileSmbInfo = null, ShareFileRequestConditions shareFileRequestConditions = null, CancellationToken cancellationToken = default) + public async Task> StartCopyAsync(string copySource, int? timeout = null, IDictionary metadata = null, string filePermission = null, FilePermissionFormat? filePermissionFormat = null, string filePermissionKey = null, CopyFileSmbInfo copyFileSmbInfo = null, ShareFileRequestConditions shareFileRequestConditions = null, CancellationToken cancellationToken = default) { if (copySource == null) { throw new ArgumentNullException(nameof(copySource)); } - using var message = CreateStartCopyRequest(copySource, timeout, metadata, filePermission, filePermissionKey, copyFileSmbInfo, shareFileRequestConditions); + using var message = CreateStartCopyRequest(copySource, timeout, metadata, filePermission, filePermissionFormat, filePermissionKey, copyFileSmbInfo, shareFileRequestConditions); await _pipeline.SendAsync(message, cancellationToken).ConfigureAwait(false); var headers = new FileStartCopyHeaders(message.Response); switch (message.Response.Status) @@ -1398,19 +1421,20 @@ public async Task> StartCopyAsync(stri /// The timeout parameter is expressed in seconds. For more information, see <a href="https://docs.microsoft.com/en-us/rest/api/storageservices/Setting-Timeouts-for-File-Service-Operations?redirectedfrom=MSDN">Setting Timeouts for File Service Operations.</a>. /// A name-value pair to associate with a file storage object. /// If specified the permission (security descriptor) shall be set for the directory/file. This header can be used if Permission size is <= 8KB, else x-ms-file-permission-key header shall be used. Default value: Inherit. If SDDL is specified as input, it must have owner, group and dacl. Note: Only one of the x-ms-file-permission or x-ms-file-permission-key should be specified. + /// Optional. Available for version 2023-06-01 and later. Specifies the format in which the permission is returned. Acceptable values are SDDL or binary. If x-ms-file-permission-format is unspecified or explicitly set to SDDL, the permission is returned in SDDL format. If x-ms-file-permission-format is explicitly set to binary, the permission is returned as a base64 string representing the binary encoding of the permission. /// Key of the permission to be set for the directory/file. Note: Only one of the x-ms-file-permission or x-ms-file-permission-key should be specified. /// Parameter group. /// Parameter group. /// The cancellation token to use. /// is null. - public ResponseWithHeaders StartCopy(string copySource, int? timeout = null, IDictionary metadata = null, string filePermission = null, string filePermissionKey = null, CopyFileSmbInfo copyFileSmbInfo = null, ShareFileRequestConditions shareFileRequestConditions = null, CancellationToken cancellationToken = default) + public ResponseWithHeaders StartCopy(string copySource, int? timeout = null, IDictionary metadata = null, string filePermission = null, FilePermissionFormat? filePermissionFormat = null, string filePermissionKey = null, CopyFileSmbInfo copyFileSmbInfo = null, ShareFileRequestConditions shareFileRequestConditions = null, CancellationToken cancellationToken = default) { if (copySource == null) { throw new ArgumentNullException(nameof(copySource)); } - using var message = CreateStartCopyRequest(copySource, timeout, metadata, filePermission, filePermissionKey, copyFileSmbInfo, shareFileRequestConditions); + using var message = CreateStartCopyRequest(copySource, timeout, metadata, filePermission, filePermissionFormat, filePermissionKey, copyFileSmbInfo, shareFileRequestConditions); _pipeline.Send(message, cancellationToken); var headers = new FileStartCopyHeaders(message.Response); switch (message.Response.Status) diff --git a/sdk/storage/Azure.Storage.Files.Shares/src/Generated/FileUploadRangeHeaders.cs b/sdk/storage/Azure.Storage.Files.Shares/src/Generated/FileUploadRangeHeaders.cs index db079c2692663..322bfcd1b6d83 100644 --- a/sdk/storage/Azure.Storage.Files.Shares/src/Generated/FileUploadRangeHeaders.cs +++ b/sdk/storage/Azure.Storage.Files.Shares/src/Generated/FileUploadRangeHeaders.cs @@ -27,5 +27,7 @@ public FileUploadRangeHeaders(Response response) public bool? IsServerEncrypted => _response.Headers.TryGetValue("x-ms-request-server-encrypted", out bool? value) ? value : null; /// Last write time for the file. public DateTimeOffset? FileLastWriteTime => _response.Headers.TryGetValue("x-ms-file-last-write-time", out DateTimeOffset? value) ? value : null; + /// Indicates the structured message body was accepted and mirrors back the message schema version and properties. + public string StructuredBodyType => _response.Headers.TryGetValue("x-ms-structured-body", out string value) ? value : null; } } diff --git a/sdk/storage/Azure.Storage.Files.Shares/src/Generated/Models/ShareAccessTier.cs b/sdk/storage/Azure.Storage.Files.Shares/src/Generated/Models/ShareAccessTier.cs index 1143539ec7749..13a41351e9229 100644 --- a/sdk/storage/Azure.Storage.Files.Shares/src/Generated/Models/ShareAccessTier.cs +++ b/sdk/storage/Azure.Storage.Files.Shares/src/Generated/Models/ShareAccessTier.cs @@ -25,6 +25,7 @@ public ShareAccessTier(string value) private const string TransactionOptimizedValue = "TransactionOptimized"; private const string HotValue = "Hot"; private const string CoolValue = "Cool"; + private const string PremiumValue = "Premium"; /// TransactionOptimized. public static ShareAccessTier TransactionOptimized { get; } = new ShareAccessTier(TransactionOptimizedValue); @@ -32,6 +33,8 @@ public ShareAccessTier(string value) public static ShareAccessTier Hot { get; } = new ShareAccessTier(HotValue); /// Cool. public static ShareAccessTier Cool { get; } = new ShareAccessTier(CoolValue); + /// Premium. + public static ShareAccessTier Premium { get; } = new ShareAccessTier(PremiumValue); /// Determines if two values are the same. public static bool operator ==(ShareAccessTier left, ShareAccessTier right) => left.Equals(right); /// Determines if two values are not the same. diff --git a/sdk/storage/Azure.Storage.Files.Shares/src/Generated/Models/ShareErrorCode.cs b/sdk/storage/Azure.Storage.Files.Shares/src/Generated/Models/ShareErrorCode.cs index 86018fa5cd082..c61baa145e37c 100644 --- a/sdk/storage/Azure.Storage.Files.Shares/src/Generated/Models/ShareErrorCode.cs +++ b/sdk/storage/Azure.Storage.Files.Shares/src/Generated/Models/ShareErrorCode.cs @@ -30,6 +30,8 @@ public ShareErrorCode(string value) private const string ConditionHeadersNotSupportedValue = "ConditionHeadersNotSupported"; private const string ConditionNotMetValue = "ConditionNotMet"; private const string EmptyMetadataKeyValue = "EmptyMetadataKey"; + private const string FileShareProvisionedBandwidthDowngradeNotAllowedValue = "FileShareProvisionedBandwidthDowngradeNotAllowed"; + private const string FileShareProvisionedIopsDowngradeNotAllowedValue = "FileShareProvisionedIopsDowngradeNotAllowed"; private const string InsufficientAccountPermissionsValue = "InsufficientAccountPermissions"; private const string InternalErrorValue = "InternalError"; private const string InvalidAuthenticationInfoValue = "InvalidAuthenticationInfo"; @@ -106,6 +108,10 @@ public ShareErrorCode(string value) public static ShareErrorCode ConditionNotMet { get; } = new ShareErrorCode(ConditionNotMetValue); /// EmptyMetadataKey. public static ShareErrorCode EmptyMetadataKey { get; } = new ShareErrorCode(EmptyMetadataKeyValue); + /// FileShareProvisionedBandwidthDowngradeNotAllowed. + public static ShareErrorCode FileShareProvisionedBandwidthDowngradeNotAllowed { get; } = new ShareErrorCode(FileShareProvisionedBandwidthDowngradeNotAllowedValue); + /// FileShareProvisionedIopsDowngradeNotAllowed. + public static ShareErrorCode FileShareProvisionedIopsDowngradeNotAllowed { get; } = new ShareErrorCode(FileShareProvisionedIopsDowngradeNotAllowedValue); /// InsufficientAccountPermissions. public static ShareErrorCode InsufficientAccountPermissions { get; } = new ShareErrorCode(InsufficientAccountPermissionsValue); /// InternalError. diff --git a/sdk/storage/Azure.Storage.Files.Shares/src/Generated/Models/SharePropertiesInternal.Serialization.cs b/sdk/storage/Azure.Storage.Files.Shares/src/Generated/Models/SharePropertiesInternal.Serialization.cs index bccf5b4b21fba..b01f216bb03d6 100644 --- a/sdk/storage/Azure.Storage.Files.Shares/src/Generated/Models/SharePropertiesInternal.Serialization.cs +++ b/sdk/storage/Azure.Storage.Files.Shares/src/Generated/Models/SharePropertiesInternal.Serialization.cs @@ -37,6 +37,10 @@ internal static SharePropertiesInternal DeserializeSharePropertiesInternal(XElem bool? paidBurstingEnabled = default; long? paidBurstingMaxIops = default; long? paidBurstingMaxBandwidthMibps = default; + long? includedBurstIops = default; + long? maxBurstCreditsForIops = default; + DateTimeOffset? nextAllowedProvisionedIopsDowngradeTime = default; + DateTimeOffset? nextAllowedProvisionedBandwidthDowngradeTime = default; if (element.Element("Last-Modified") is XElement lastModifiedElement) { lastModified = lastModifiedElement.GetDateTimeOffsetValue("R"); @@ -125,6 +129,22 @@ internal static SharePropertiesInternal DeserializeSharePropertiesInternal(XElem { paidBurstingMaxBandwidthMibps = (long?)paidBurstingMaxBandwidthMibpsElement; } + if (element.Element("IncludedBurstIops") is XElement includedBurstIopsElement) + { + includedBurstIops = (long?)includedBurstIopsElement; + } + if (element.Element("MaxBurstCreditsForIops") is XElement maxBurstCreditsForIopsElement) + { + maxBurstCreditsForIops = (long?)maxBurstCreditsForIopsElement; + } + if (element.Element("NextAllowedProvisionedIopsDowngradeTime") is XElement nextAllowedProvisionedIopsDowngradeTimeElement) + { + nextAllowedProvisionedIopsDowngradeTime = nextAllowedProvisionedIopsDowngradeTimeElement.GetDateTimeOffsetValue("R"); + } + if (element.Element("NextAllowedProvisionedBandwidthDowngradeTime") is XElement nextAllowedProvisionedBandwidthDowngradeTimeElement) + { + nextAllowedProvisionedBandwidthDowngradeTime = nextAllowedProvisionedBandwidthDowngradeTimeElement.GetDateTimeOffsetValue("R"); + } return new SharePropertiesInternal( lastModified, etag, @@ -147,7 +167,11 @@ internal static SharePropertiesInternal DeserializeSharePropertiesInternal(XElem enableSnapshotVirtualDirectoryAccess, paidBurstingEnabled, paidBurstingMaxIops, - paidBurstingMaxBandwidthMibps); + paidBurstingMaxBandwidthMibps, + includedBurstIops, + maxBurstCreditsForIops, + nextAllowedProvisionedIopsDowngradeTime, + nextAllowedProvisionedBandwidthDowngradeTime); } } } diff --git a/sdk/storage/Azure.Storage.Files.Shares/src/Generated/Models/SharePropertiesInternal.cs b/sdk/storage/Azure.Storage.Files.Shares/src/Generated/Models/SharePropertiesInternal.cs index b550b3e07bd7e..eeb13840f2042 100644 --- a/sdk/storage/Azure.Storage.Files.Shares/src/Generated/Models/SharePropertiesInternal.cs +++ b/sdk/storage/Azure.Storage.Files.Shares/src/Generated/Models/SharePropertiesInternal.cs @@ -50,7 +50,11 @@ internal SharePropertiesInternal(DateTimeOffset lastModified, string etag, int q /// /// /// - internal SharePropertiesInternal(DateTimeOffset lastModified, string etag, int quota, int? provisionedIops, int? provisionedIngressMBps, int? provisionedEgressMBps, int? provisionedBandwidthMiBps, DateTimeOffset? nextAllowedQuotaDowngradeTime, DateTimeOffset? deletedTime, int? remainingRetentionDays, string accessTier, DateTimeOffset? accessTierChangeTime, string accessTierTransitionState, ShareLeaseStatus? leaseStatus, ShareLeaseState? leaseState, ShareLeaseDuration? leaseDuration, string enabledProtocols, ShareRootSquash? rootSquash, bool? enableSnapshotVirtualDirectoryAccess, bool? paidBurstingEnabled, long? paidBurstingMaxIops, long? paidBurstingMaxBandwidthMibps) + /// + /// + /// + /// + internal SharePropertiesInternal(DateTimeOffset lastModified, string etag, int quota, int? provisionedIops, int? provisionedIngressMBps, int? provisionedEgressMBps, int? provisionedBandwidthMiBps, DateTimeOffset? nextAllowedQuotaDowngradeTime, DateTimeOffset? deletedTime, int? remainingRetentionDays, string accessTier, DateTimeOffset? accessTierChangeTime, string accessTierTransitionState, ShareLeaseStatus? leaseStatus, ShareLeaseState? leaseState, ShareLeaseDuration? leaseDuration, string enabledProtocols, ShareRootSquash? rootSquash, bool? enableSnapshotVirtualDirectoryAccess, bool? paidBurstingEnabled, long? paidBurstingMaxIops, long? paidBurstingMaxBandwidthMibps, long? includedBurstIops, long? maxBurstCreditsForIops, DateTimeOffset? nextAllowedProvisionedIopsDowngradeTime, DateTimeOffset? nextAllowedProvisionedBandwidthDowngradeTime) { LastModified = lastModified; Etag = etag; @@ -74,6 +78,10 @@ internal SharePropertiesInternal(DateTimeOffset lastModified, string etag, int q PaidBurstingEnabled = paidBurstingEnabled; PaidBurstingMaxIops = paidBurstingMaxIops; PaidBurstingMaxBandwidthMibps = paidBurstingMaxBandwidthMibps; + IncludedBurstIops = includedBurstIops; + MaxBurstCreditsForIops = maxBurstCreditsForIops; + NextAllowedProvisionedIopsDowngradeTime = nextAllowedProvisionedIopsDowngradeTime; + NextAllowedProvisionedBandwidthDowngradeTime = nextAllowedProvisionedBandwidthDowngradeTime; } /// Gets the last modified. @@ -120,5 +128,13 @@ internal SharePropertiesInternal(DateTimeOffset lastModified, string etag, int q public long? PaidBurstingMaxIops { get; } /// Gets the paid bursting max bandwidth mibps. public long? PaidBurstingMaxBandwidthMibps { get; } + /// Gets the included burst iops. + public long? IncludedBurstIops { get; } + /// Gets the max burst credits for iops. + public long? MaxBurstCreditsForIops { get; } + /// Gets the next allowed provisioned iops downgrade time. + public DateTimeOffset? NextAllowedProvisionedIopsDowngradeTime { get; } + /// Gets the next allowed provisioned bandwidth downgrade time. + public DateTimeOffset? NextAllowedProvisionedBandwidthDowngradeTime { get; } } } diff --git a/sdk/storage/Azure.Storage.Files.Shares/src/Generated/ServiceRestClient.cs b/sdk/storage/Azure.Storage.Files.Shares/src/Generated/ServiceRestClient.cs index ef4c21b9a33c7..fe5ea495a7a15 100644 --- a/sdk/storage/Azure.Storage.Files.Shares/src/Generated/ServiceRestClient.cs +++ b/sdk/storage/Azure.Storage.Files.Shares/src/Generated/ServiceRestClient.cs @@ -31,7 +31,7 @@ internal partial class ServiceRestClient /// The handler for diagnostic messaging in the client. /// The HTTP pipeline for sending and receiving REST requests and responses. /// The URL of the service account, share, directory or file that is the target of the desired operation. - /// Specifies the version of the operation to use for this request. The default value is "2024-11-04". + /// Specifies the version of the operation to use for this request. The default value is "2025-01-05". /// Valid value is backup. /// , , or is null. public ServiceRestClient(ClientDiagnostics clientDiagnostics, HttpPipeline pipeline, string url, string version, ShareTokenIntent? fileRequestIntent = null) diff --git a/sdk/storage/Azure.Storage.Files.Shares/src/Generated/ShareCreateHeaders.cs b/sdk/storage/Azure.Storage.Files.Shares/src/Generated/ShareCreateHeaders.cs index 6f22731fa70ca..c06e37fdb62dc 100644 --- a/sdk/storage/Azure.Storage.Files.Shares/src/Generated/ShareCreateHeaders.cs +++ b/sdk/storage/Azure.Storage.Files.Shares/src/Generated/ShareCreateHeaders.cs @@ -21,5 +21,15 @@ public ShareCreateHeaders(Response response) public DateTimeOffset? LastModified => _response.Headers.TryGetValue("Last-Modified", out DateTimeOffset? value) ? value : null; /// Indicates the version of the File service used to execute the request. public string Version => _response.Headers.TryGetValue("x-ms-version", out string value) ? value : null; + /// Returns the current share quota in GB. + public long? Quota => _response.Headers.TryGetValue("x-ms-share-quota", out long? value) ? value : null; + /// The provisioned IOPS of the share. + public long? ShareProvisionedIops => _response.Headers.TryGetValue("x-ms-share-provisioned-iops", out long? value) ? value : null; + /// The provisioned throughput of the share. + public long? ShareProvisionedBandwidthMibps => _response.Headers.TryGetValue("x-ms-share-provisioned-bandwidth-mibps", out long? value) ? value : null; + /// Returns the calculated burst IOPS of the share. + public long? ShareIncludedBurstIops => _response.Headers.TryGetValue("x-ms-share-included-burst-iops", out long? value) ? value : null; + /// Returned the calculated maximum burst credits. + public long? MaxBurstCreditsForIops => _response.Headers.TryGetValue("x-ms-share-max-burst-credits-for-iops", out long? value) ? value : null; } } diff --git a/sdk/storage/Azure.Storage.Files.Shares/src/Generated/ShareDeleteHeaders.cs b/sdk/storage/Azure.Storage.Files.Shares/src/Generated/ShareDeleteHeaders.cs index 980991becbeb8..c0521e8c8e75a 100644 --- a/sdk/storage/Azure.Storage.Files.Shares/src/Generated/ShareDeleteHeaders.cs +++ b/sdk/storage/Azure.Storage.Files.Shares/src/Generated/ShareDeleteHeaders.cs @@ -18,5 +18,9 @@ public ShareDeleteHeaders(Response response) } /// Indicates the version of the File service used to execute the request. public string Version => _response.Headers.TryGetValue("x-ms-version", out string value) ? value : null; + /// Returned only for provisioned v2 file shares. Returns an approximate used storage size of the share, in bytes. + public long? XMsFileShareUsageBytes => _response.Headers.TryGetValue("x-ms-file-share-usage-bytes", out long? value) ? value : null; + /// Returned only for provisioned v2 file shares. Returns an approximate used snapshot storage size of the share, in bytes. + public long? XMsFileShareSnapshotUsageBytes => _response.Headers.TryGetValue("x-ms-file-share-snapshot-usage-bytes", out long? value) ? value : null; } } diff --git a/sdk/storage/Azure.Storage.Files.Shares/src/Generated/ShareGetPropertiesHeaders.cs b/sdk/storage/Azure.Storage.Files.Shares/src/Generated/ShareGetPropertiesHeaders.cs index 86a01b76969d8..a0a18081ee687 100644 --- a/sdk/storage/Azure.Storage.Files.Shares/src/Generated/ShareGetPropertiesHeaders.cs +++ b/sdk/storage/Azure.Storage.Files.Shares/src/Generated/ShareGetPropertiesHeaders.cs @@ -35,7 +35,7 @@ public ShareGetPropertiesHeaders(Response response) public int? ProvisionedEgressMBps => _response.Headers.TryGetValue("x-ms-share-provisioned-egress-mbps", out int? value) ? value : null; /// Returns the current share next allowed quota downgrade time. public DateTimeOffset? NextAllowedQuotaDowngradeTime => _response.Headers.TryGetValue("x-ms-share-next-allowed-quota-downgrade-time", out DateTimeOffset? value) ? value : null; - /// Returns the current share provisioned bandwidth in megabits per second. + /// Returns the current share provisioned bandwidth in mebibytes per second. public int? ProvisionedBandwidthMibps => _response.Headers.TryGetValue("x-ms-share-provisioned-bandwidth-mibps", out int? value) ? value : null; /// When a share is leased, specifies whether the lease is of infinite or fixed duration. public ShareLeaseDuration? LeaseDuration => _response.Headers.TryGetValue("x-ms-lease-duration", out string value) ? value.ToShareLeaseDuration() : null; @@ -61,5 +61,13 @@ public ShareGetPropertiesHeaders(Response response) public long? PaidBurstingMaxIops => _response.Headers.TryGetValue("x-ms-share-paid-bursting-max-iops", out long? value) ? value : null; /// Optional. Integer. Default if not specified is the maximum throughput the file share can support. Current maximum for a file share is 10,340 MiB/sec. public long? PaidBurstingMaxBandwidthMibps => _response.Headers.TryGetValue("x-ms-share-paid-bursting-max-bandwidth-mibps", out long? value) ? value : null; + /// Return the calculated burst IOPS of the share. + public long? IncludedBurstIops => _response.Headers.TryGetValue("x-ms-share-included-burst-iops", out long? value) ? value : null; + /// Returned the calculated maximum burst credits. This is not the current burst credit level, but the maximum burst credits the share can have. + public long? MaxBurstCreditsForIops => _response.Headers.TryGetValue("x-ms-share-max-burst-credits-for-iops", out long? value) ? value : null; + /// Returns the current share next allowed provisioned iops downgrade time. + public DateTimeOffset? NextAllowedProvisionedIopsDowngradeTime => _response.Headers.TryGetValue("x-ms-share-next-allowed-provisioned-iops-downgrade-time", out DateTimeOffset? value) ? value : null; + /// Returns the current share next allowed provisioned bandwidth downgrade time. + public DateTimeOffset? NextAllowedProvisionedBandwidthDowngradeTime => _response.Headers.TryGetValue("x-ms-share-next-allowed-provisioned-bandwidth-downgrade-time", out DateTimeOffset? value) ? value : null; } } diff --git a/sdk/storage/Azure.Storage.Files.Shares/src/Generated/ShareRestClient.cs b/sdk/storage/Azure.Storage.Files.Shares/src/Generated/ShareRestClient.cs index 599aacf2c6287..69bb02404dd49 100644 --- a/sdk/storage/Azure.Storage.Files.Shares/src/Generated/ShareRestClient.cs +++ b/sdk/storage/Azure.Storage.Files.Shares/src/Generated/ShareRestClient.cs @@ -32,7 +32,7 @@ internal partial class ShareRestClient /// The handler for diagnostic messaging in the client. /// The HTTP pipeline for sending and receiving REST requests and responses. /// The URL of the service account, share, directory or file that is the target of the desired operation. - /// Specifies the version of the operation to use for this request. The default value is "2024-11-04". + /// Specifies the version of the operation to use for this request. The default value is "2025-01-05". /// Valid value is backup. /// , , or is null. public ShareRestClient(ClientDiagnostics clientDiagnostics, HttpPipeline pipeline, string url, string version, ShareTokenIntent? fileRequestIntent = null) @@ -44,7 +44,7 @@ public ShareRestClient(ClientDiagnostics clientDiagnostics, HttpPipeline pipelin _fileRequestIntent = fileRequestIntent; } - internal HttpMessage CreateCreateRequest(int? timeout, IDictionary metadata, int? quota, ShareAccessTier? accessTier, string enabledProtocols, ShareRootSquash? rootSquash, bool? enableSnapshotVirtualDirectoryAccess, bool? paidBurstingEnabled, long? paidBurstingMaxBandwidthMibps, long? paidBurstingMaxIops) + internal HttpMessage CreateCreateRequest(int? timeout, IDictionary metadata, int? quota, ShareAccessTier? accessTier, string enabledProtocols, ShareRootSquash? rootSquash, bool? enableSnapshotVirtualDirectoryAccess, bool? paidBurstingEnabled, long? paidBurstingMaxBandwidthMibps, long? paidBurstingMaxIops, long? shareProvisionedIops, long? shareProvisionedBandwidthMibps) { var message = _pipeline.CreateMessage(); var request = message.Request; @@ -98,6 +98,14 @@ internal HttpMessage CreateCreateRequest(int? timeout, IDictionary Optional. Boolean. Default if not specified is false. This property enables paid bursting. /// Optional. Integer. Default if not specified is the maximum throughput the file share can support. Current maximum for a file share is 10,340 MiB/sec. /// Optional. Integer. Default if not specified is the maximum IOPS the file share can support. Current maximum for a file share is 102,400 IOPS. + /// Optional. Supported in version 2025-01-05 and later. Only allowed for provisioned v2 file shares. Specifies the provisioned number of input/output operations per second (IOPS) of the share. If this is not specified, the provisioned IOPS is set to value calculated based on recommendation formula. + /// Optional. Supported in version 2025-01-05 and later. Only allowed for provisioned v2 file shares. Specifies the provisioned bandwidth of the share, in mebibytes per second (MiBps). If this is not specified, the provisioned bandwidth is set to value calculated based on recommendation formula. /// The cancellation token to use. - public async Task> CreateAsync(int? timeout = null, IDictionary metadata = null, int? quota = null, ShareAccessTier? accessTier = null, string enabledProtocols = null, ShareRootSquash? rootSquash = null, bool? enableSnapshotVirtualDirectoryAccess = null, bool? paidBurstingEnabled = null, long? paidBurstingMaxBandwidthMibps = null, long? paidBurstingMaxIops = null, CancellationToken cancellationToken = default) + public async Task> CreateAsync(int? timeout = null, IDictionary metadata = null, int? quota = null, ShareAccessTier? accessTier = null, string enabledProtocols = null, ShareRootSquash? rootSquash = null, bool? enableSnapshotVirtualDirectoryAccess = null, bool? paidBurstingEnabled = null, long? paidBurstingMaxBandwidthMibps = null, long? paidBurstingMaxIops = null, long? shareProvisionedIops = null, long? shareProvisionedBandwidthMibps = null, CancellationToken cancellationToken = default) { - using var message = CreateCreateRequest(timeout, metadata, quota, accessTier, enabledProtocols, rootSquash, enableSnapshotVirtualDirectoryAccess, paidBurstingEnabled, paidBurstingMaxBandwidthMibps, paidBurstingMaxIops); + using var message = CreateCreateRequest(timeout, metadata, quota, accessTier, enabledProtocols, rootSquash, enableSnapshotVirtualDirectoryAccess, paidBurstingEnabled, paidBurstingMaxBandwidthMibps, paidBurstingMaxIops, shareProvisionedIops, shareProvisionedBandwidthMibps); await _pipeline.SendAsync(message, cancellationToken).ConfigureAwait(false); var headers = new ShareCreateHeaders(message.Response); switch (message.Response.Status) @@ -139,10 +149,12 @@ public async Task> CreateAsync(int? time /// Optional. Boolean. Default if not specified is false. This property enables paid bursting. /// Optional. Integer. Default if not specified is the maximum throughput the file share can support. Current maximum for a file share is 10,340 MiB/sec. /// Optional. Integer. Default if not specified is the maximum IOPS the file share can support. Current maximum for a file share is 102,400 IOPS. + /// Optional. Supported in version 2025-01-05 and later. Only allowed for provisioned v2 file shares. Specifies the provisioned number of input/output operations per second (IOPS) of the share. If this is not specified, the provisioned IOPS is set to value calculated based on recommendation formula. + /// Optional. Supported in version 2025-01-05 and later. Only allowed for provisioned v2 file shares. Specifies the provisioned bandwidth of the share, in mebibytes per second (MiBps). If this is not specified, the provisioned bandwidth is set to value calculated based on recommendation formula. /// The cancellation token to use. - public ResponseWithHeaders Create(int? timeout = null, IDictionary metadata = null, int? quota = null, ShareAccessTier? accessTier = null, string enabledProtocols = null, ShareRootSquash? rootSquash = null, bool? enableSnapshotVirtualDirectoryAccess = null, bool? paidBurstingEnabled = null, long? paidBurstingMaxBandwidthMibps = null, long? paidBurstingMaxIops = null, CancellationToken cancellationToken = default) + public ResponseWithHeaders Create(int? timeout = null, IDictionary metadata = null, int? quota = null, ShareAccessTier? accessTier = null, string enabledProtocols = null, ShareRootSquash? rootSquash = null, bool? enableSnapshotVirtualDirectoryAccess = null, bool? paidBurstingEnabled = null, long? paidBurstingMaxBandwidthMibps = null, long? paidBurstingMaxIops = null, long? shareProvisionedIops = null, long? shareProvisionedBandwidthMibps = null, CancellationToken cancellationToken = default) { - using var message = CreateCreateRequest(timeout, metadata, quota, accessTier, enabledProtocols, rootSquash, enableSnapshotVirtualDirectoryAccess, paidBurstingEnabled, paidBurstingMaxBandwidthMibps, paidBurstingMaxIops); + using var message = CreateCreateRequest(timeout, metadata, quota, accessTier, enabledProtocols, rootSquash, enableSnapshotVirtualDirectoryAccess, paidBurstingEnabled, paidBurstingMaxBandwidthMibps, paidBurstingMaxIops, shareProvisionedIops, shareProvisionedBandwidthMibps); _pipeline.Send(message, cancellationToken); var headers = new ShareCreateHeaders(message.Response); switch (message.Response.Status) @@ -917,7 +929,7 @@ public ResponseWithHeaders GetPermis } } - internal HttpMessage CreateSetPropertiesRequest(int? timeout, int? quota, ShareAccessTier? accessTier, ShareRootSquash? rootSquash, bool? enableSnapshotVirtualDirectoryAccess, bool? paidBurstingEnabled, long? paidBurstingMaxBandwidthMibps, long? paidBurstingMaxIops, ShareFileRequestConditions shareFileRequestConditions) + internal HttpMessage CreateSetPropertiesRequest(int? timeout, int? quota, ShareAccessTier? accessTier, ShareRootSquash? rootSquash, bool? enableSnapshotVirtualDirectoryAccess, bool? paidBurstingEnabled, long? paidBurstingMaxBandwidthMibps, long? paidBurstingMaxIops, long? shareProvisionedIops, long? shareProvisionedBandwidthMibps, ShareFileRequestConditions shareFileRequestConditions) { var message = _pipeline.CreateMessage(); var request = message.Request; @@ -968,6 +980,14 @@ internal HttpMessage CreateSetPropertiesRequest(int? timeout, int? quota, ShareA { request.Headers.Add("x-ms-file-request-intent", _fileRequestIntent.Value.ToString()); } + if (shareProvisionedIops != null) + { + request.Headers.Add("x-ms-share-provisioned-iops", shareProvisionedIops.Value); + } + if (shareProvisionedBandwidthMibps != null) + { + request.Headers.Add("x-ms-share-provisioned-bandwidth-mibps", shareProvisionedBandwidthMibps.Value); + } request.Headers.Add("Accept", "application/xml"); return message; } @@ -981,11 +1001,13 @@ internal HttpMessage CreateSetPropertiesRequest(int? timeout, int? quota, ShareA /// Optional. Boolean. Default if not specified is false. This property enables paid bursting. /// Optional. Integer. Default if not specified is the maximum throughput the file share can support. Current maximum for a file share is 10,340 MiB/sec. /// Optional. Integer. Default if not specified is the maximum IOPS the file share can support. Current maximum for a file share is 102,400 IOPS. + /// Optional. Supported in version 2025-01-05 and later. Only allowed for provisioned v2 file shares. Specifies the provisioned number of input/output operations per second (IOPS) of the share. If this is not specified, the provisioned IOPS is set to value calculated based on recommendation formula. + /// Optional. Supported in version 2025-01-05 and later. Only allowed for provisioned v2 file shares. Specifies the provisioned bandwidth of the share, in mebibytes per second (MiBps). If this is not specified, the provisioned bandwidth is set to value calculated based on recommendation formula. /// Parameter group. /// The cancellation token to use. - public async Task> SetPropertiesAsync(int? timeout = null, int? quota = null, ShareAccessTier? accessTier = null, ShareRootSquash? rootSquash = null, bool? enableSnapshotVirtualDirectoryAccess = null, bool? paidBurstingEnabled = null, long? paidBurstingMaxBandwidthMibps = null, long? paidBurstingMaxIops = null, ShareFileRequestConditions shareFileRequestConditions = null, CancellationToken cancellationToken = default) + public async Task> SetPropertiesAsync(int? timeout = null, int? quota = null, ShareAccessTier? accessTier = null, ShareRootSquash? rootSquash = null, bool? enableSnapshotVirtualDirectoryAccess = null, bool? paidBurstingEnabled = null, long? paidBurstingMaxBandwidthMibps = null, long? paidBurstingMaxIops = null, long? shareProvisionedIops = null, long? shareProvisionedBandwidthMibps = null, ShareFileRequestConditions shareFileRequestConditions = null, CancellationToken cancellationToken = default) { - using var message = CreateSetPropertiesRequest(timeout, quota, accessTier, rootSquash, enableSnapshotVirtualDirectoryAccess, paidBurstingEnabled, paidBurstingMaxBandwidthMibps, paidBurstingMaxIops, shareFileRequestConditions); + using var message = CreateSetPropertiesRequest(timeout, quota, accessTier, rootSquash, enableSnapshotVirtualDirectoryAccess, paidBurstingEnabled, paidBurstingMaxBandwidthMibps, paidBurstingMaxIops, shareProvisionedIops, shareProvisionedBandwidthMibps, shareFileRequestConditions); await _pipeline.SendAsync(message, cancellationToken).ConfigureAwait(false); var headers = new ShareSetPropertiesHeaders(message.Response); switch (message.Response.Status) @@ -1006,11 +1028,13 @@ public async Task> SetPropertiesA /// Optional. Boolean. Default if not specified is false. This property enables paid bursting. /// Optional. Integer. Default if not specified is the maximum throughput the file share can support. Current maximum for a file share is 10,340 MiB/sec. /// Optional. Integer. Default if not specified is the maximum IOPS the file share can support. Current maximum for a file share is 102,400 IOPS. + /// Optional. Supported in version 2025-01-05 and later. Only allowed for provisioned v2 file shares. Specifies the provisioned number of input/output operations per second (IOPS) of the share. If this is not specified, the provisioned IOPS is set to value calculated based on recommendation formula. + /// Optional. Supported in version 2025-01-05 and later. Only allowed for provisioned v2 file shares. Specifies the provisioned bandwidth of the share, in mebibytes per second (MiBps). If this is not specified, the provisioned bandwidth is set to value calculated based on recommendation formula. /// Parameter group. /// The cancellation token to use. - public ResponseWithHeaders SetProperties(int? timeout = null, int? quota = null, ShareAccessTier? accessTier = null, ShareRootSquash? rootSquash = null, bool? enableSnapshotVirtualDirectoryAccess = null, bool? paidBurstingEnabled = null, long? paidBurstingMaxBandwidthMibps = null, long? paidBurstingMaxIops = null, ShareFileRequestConditions shareFileRequestConditions = null, CancellationToken cancellationToken = default) + public ResponseWithHeaders SetProperties(int? timeout = null, int? quota = null, ShareAccessTier? accessTier = null, ShareRootSquash? rootSquash = null, bool? enableSnapshotVirtualDirectoryAccess = null, bool? paidBurstingEnabled = null, long? paidBurstingMaxBandwidthMibps = null, long? paidBurstingMaxIops = null, long? shareProvisionedIops = null, long? shareProvisionedBandwidthMibps = null, ShareFileRequestConditions shareFileRequestConditions = null, CancellationToken cancellationToken = default) { - using var message = CreateSetPropertiesRequest(timeout, quota, accessTier, rootSquash, enableSnapshotVirtualDirectoryAccess, paidBurstingEnabled, paidBurstingMaxBandwidthMibps, paidBurstingMaxIops, shareFileRequestConditions); + using var message = CreateSetPropertiesRequest(timeout, quota, accessTier, rootSquash, enableSnapshotVirtualDirectoryAccess, paidBurstingEnabled, paidBurstingMaxBandwidthMibps, paidBurstingMaxIops, shareProvisionedIops, shareProvisionedBandwidthMibps, shareFileRequestConditions); _pipeline.Send(message, cancellationToken); var headers = new ShareSetPropertiesHeaders(message.Response); switch (message.Response.Status) diff --git a/sdk/storage/Azure.Storage.Files.Shares/src/Generated/ShareRestoreHeaders.cs b/sdk/storage/Azure.Storage.Files.Shares/src/Generated/ShareRestoreHeaders.cs index cd5411eee99d0..7cbb4e511e775 100644 --- a/sdk/storage/Azure.Storage.Files.Shares/src/Generated/ShareRestoreHeaders.cs +++ b/sdk/storage/Azure.Storage.Files.Shares/src/Generated/ShareRestoreHeaders.cs @@ -21,5 +21,15 @@ public ShareRestoreHeaders(Response response) public DateTimeOffset? LastModified => _response.Headers.TryGetValue("Last-Modified", out DateTimeOffset? value) ? value : null; /// Indicates the version of the File service used to execute the request. public string Version => _response.Headers.TryGetValue("x-ms-version", out string value) ? value : null; + /// Returns the current share quota in GB. + public long? Quota => _response.Headers.TryGetValue("x-ms-share-quota", out long? value) ? value : null; + /// Returns the current share provisioned ipos. + public long? ProvisionedIops => _response.Headers.TryGetValue("x-ms-share-provisioned-iops", out long? value) ? value : null; + /// Returns the current share provisioned bandwidth in mebibytes per second. + public long? ProvisionedBandwidthMibps => _response.Headers.TryGetValue("x-ms-share-provisioned-bandwidth-mibps", out long? value) ? value : null; + /// Return the calculated burst IOPS of the share. + public long? IncludedBurstIops => _response.Headers.TryGetValue("x-ms-share-included-burst-iops", out long? value) ? value : null; + /// Returned the calculated maximum burst credits. This is not the current burst credit level, but the maximum burst credits the share can have. + public long? MaxBurstCreditsForIops => _response.Headers.TryGetValue("x-ms-share-max-burst-credits-for-iops", out long? value) ? value : null; } } diff --git a/sdk/storage/Azure.Storage.Files.Shares/src/Generated/ShareSetPropertiesHeaders.cs b/sdk/storage/Azure.Storage.Files.Shares/src/Generated/ShareSetPropertiesHeaders.cs index 85e942d84cf1f..d12ce08289db9 100644 --- a/sdk/storage/Azure.Storage.Files.Shares/src/Generated/ShareSetPropertiesHeaders.cs +++ b/sdk/storage/Azure.Storage.Files.Shares/src/Generated/ShareSetPropertiesHeaders.cs @@ -21,5 +21,21 @@ public ShareSetPropertiesHeaders(Response response) public DateTimeOffset? LastModified => _response.Headers.TryGetValue("Last-Modified", out DateTimeOffset? value) ? value : null; /// Indicates the version of the File service used to execute the request. public string Version => _response.Headers.TryGetValue("x-ms-version", out string value) ? value : null; + /// Returns the current share quota in GB. + public long? Quota => _response.Headers.TryGetValue("x-ms-share-quota", out long? value) ? value : null; + /// Returns the current share provisioned ipos. + public long? ProvisionedIops => _response.Headers.TryGetValue("x-ms-share-provisioned-iops", out long? value) ? value : null; + /// Returns the current share provisioned bandwidth in mebibytes per second. + public long? ProvisionedBandwidthMibps => _response.Headers.TryGetValue("x-ms-share-provisioned-bandwidth-mibps", out long? value) ? value : null; + /// Return the calculated burst IOPS of the share. + public long? IncludedBurstIops => _response.Headers.TryGetValue("x-ms-share-included-burst-iops", out long? value) ? value : null; + /// Returned the calculated maximum burst credits. This is not the current burst credit level, but the maximum burst credits the share can have. + public long? MaxBurstCreditsForIops => _response.Headers.TryGetValue("x-ms-share-max-burst-credits-for-iops", out long? value) ? value : null; + /// Returns the current share next allowed quota downgrade time. + public DateTimeOffset? NextAllowedQuotaDowngradeTime => _response.Headers.TryGetValue("x-ms-share-next-allowed-quota-downgrade-time", out DateTimeOffset? value) ? value : null; + /// Returns the current share next allowed provisioned iops downgrade time. + public DateTimeOffset? NextAllowedProvisionedIopsDowngradeTime => _response.Headers.TryGetValue("x-ms-share-next-allowed-provisioned-iops-downgrade-time", out DateTimeOffset? value) ? value : null; + /// Returns the current share next allowed provisioned bandwidth downgrade time. + public DateTimeOffset? NextAllowedProvisionedBandwidthDowngradeTime => _response.Headers.TryGetValue("x-ms-share-next-allowed-provisioned-bandwidth-downgrade-time", out DateTimeOffset? value) ? value : null; } } diff --git a/sdk/storage/Azure.Storage.Files.Shares/src/Models/ShareCreateOptions.cs b/sdk/storage/Azure.Storage.Files.Shares/src/Models/ShareCreateOptions.cs index 5da39cf3fb5cc..39ebe5d6ea003 100644 --- a/sdk/storage/Azure.Storage.Files.Shares/src/Models/ShareCreateOptions.cs +++ b/sdk/storage/Azure.Storage.Files.Shares/src/Models/ShareCreateOptions.cs @@ -64,5 +64,18 @@ public class ShareCreateOptions /// Default if not specified is the maximum throughput the file share can support. Current maximum for a file share is 10,340 MiB/sec. /// public long? PaidBurstingMaxBandwidthMibps { get; set; } + + /// + /// Optional. Only applicable to provisioned v2 storage accounts. + /// The provisioned IOPS of the share. For SSD, minimum IOPS is 3,000 and maximum is 100,000. For HDD, minimum IOPS is 500 and maximum is 50,000. + /// + public long? ProvisionedMaxIops { get; set; } + + /// + /// Optional. Only applicable to provisioned v2 storage accounts. + /// The provisioned throughput of the share. For SSD, minimum throughput is 125 MiB/sec and maximum is 10,340 MiB/sec. + /// For HDD, minimum throughput is 60 MiB/sec and maximum is 5,125 MiB/sec. + /// + public long? ProvisionedMaxBandwidthMibps { get; set; } } } diff --git a/sdk/storage/Azure.Storage.Files.Shares/src/Models/ShareFileCopyOptions.cs b/sdk/storage/Azure.Storage.Files.Shares/src/Models/ShareFileCopyOptions.cs index db7f3bc017ab9..d74113002f182 100644 --- a/sdk/storage/Azure.Storage.Files.Shares/src/Models/ShareFileCopyOptions.cs +++ b/sdk/storage/Azure.Storage.Files.Shares/src/Models/ShareFileCopyOptions.cs @@ -27,6 +27,13 @@ public class ShareFileCopyOptions /// public string FilePermission { get; set; } + /// + /// Specifies the format in which the file permission is returned. If unspecified or explicitly set to SDDL, + /// the permission is returned in SDDL format. If explicitly set to binary, the permission is returned as a base64 + /// string representing the binary encoding of the permission. + /// + public FilePermissionFormat? PermissionFormat { get; set; } + /// /// Specifies the option to copy file security descriptor from source file or /// to set it using the value which is defined by the header value of FilePermission diff --git a/sdk/storage/Azure.Storage.Files.Shares/src/Models/ShareModelFactory.cs b/sdk/storage/Azure.Storage.Files.Shares/src/Models/ShareModelFactory.cs index 618fdfc0c6aba..a708316d026fc 100644 --- a/sdk/storage/Azure.Storage.Files.Shares/src/Models/ShareModelFactory.cs +++ b/sdk/storage/Azure.Storage.Files.Shares/src/Models/ShareModelFactory.cs @@ -48,7 +48,68 @@ public static ShareProperties ShareProperties( bool? enableSnapshotVirtualDirectoryAccess = default, bool? enablePaidBursting = default, long? paidBurstingMaxIops = default, - long? paidBustingMaxBandwidthMibps = default) + long? paidBustingMaxBandwidthMibps = default, + long? includedBurstIops = default, + long? maxBurstCreditsForIops = default, + DateTimeOffset? nextAllowedProvisionedIopsDowngradeTime = default, + DateTimeOffset? nextAllowedProvisionedBandwidthDowngradeTime = default) + => new ShareProperties() + { + AccessTier = accessTier, + LastModified = lastModified, + ProvisionedIops = provisionedIops, + ProvisionedIngressMBps = provisionedIngressMBps, + ProvisionedEgressMBps = provisionedEgressMBps, + NextAllowedQuotaDowngradeTime = nextAllowedQuotaDowngradeTime, + DeletedOn = deletedOn, + RemainingRetentionDays = remainingRetentionDays, + ETag = eTag, + AccessTierChangeTime = accessTierChangeTime, + AccessTierTransitionState = accessTierTransitionState, + LeaseStatus = leaseStatus, + LeaseState = leaseState, + LeaseDuration = leaseDuration, + QuotaInGB = quotaInGB, + Metadata = metadata, + Protocols = protocols, + RootSquash = rootSquash, + EnableSnapshotVirtualDirectoryAccess = enableSnapshotVirtualDirectoryAccess, + EnablePaidBursting = enablePaidBursting, + PaidBurstingMaxIops = paidBurstingMaxIops, + PaidBurstingMaxBandwidthMibps = paidBustingMaxBandwidthMibps, + IncludedBurstIops = includedBurstIops, + MaxBurstCreditsForIops = maxBurstCreditsForIops, + NextAllowedProvisionedIopsDowngradeTime = nextAllowedProvisionedIopsDowngradeTime, + NextAllowedProvisionedBandwidthDowngradeTime = nextAllowedProvisionedBandwidthDowngradeTime, + }; + + /// + /// Creates a new ShareProperties instance for mocking. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public static ShareProperties ShareProperties( + string accessTier, + DateTimeOffset? lastModified, + int? provisionedIops, + int? provisionedIngressMBps, + int? provisionedEgressMBps, + DateTimeOffset? nextAllowedQuotaDowngradeTime, + DateTimeOffset? deletedOn, + int? remainingRetentionDays, + ETag? eTag, + DateTimeOffset? accessTierChangeTime, + string accessTierTransitionState, + ShareLeaseStatus? leaseStatus, + ShareLeaseState? leaseState, + ShareLeaseDuration? leaseDuration, + int? quotaInGB, + IDictionary metadata, + ShareProtocols? protocols, + ShareRootSquash? rootSquash, + bool? enableSnapshotVirtualDirectoryAccess, + bool? enablePaidBursting, + long? paidBurstingMaxIops, + long? paidBustingMaxBandwidthMibps) => new ShareProperties() { AccessTier = accessTier, diff --git a/sdk/storage/Azure.Storage.Files.Shares/src/Models/ShareProperties.cs b/sdk/storage/Azure.Storage.Files.Shares/src/Models/ShareProperties.cs index 743ad88232164..ef2ec2569f595 100644 --- a/sdk/storage/Azure.Storage.Files.Shares/src/Models/ShareProperties.cs +++ b/sdk/storage/Azure.Storage.Files.Shares/src/Models/ShareProperties.cs @@ -137,6 +137,30 @@ public class ShareProperties /// public long? PaidBurstingMaxBandwidthMibps { get; internal set; } + /// + /// Only applicable to provisioned v2 storage accounts. + /// The calculated burst IOPS of the share. + /// + public long? IncludedBurstIops { get; internal set; } + + /// + /// Only applicable to provisioned v2 storage accounts. + /// The calculated maximum burst credits. This is not the current burst credit level, but the maximum burst credits the share can have. + /// + public long? MaxBurstCreditsForIops { get; internal set; } + + /// + /// Only applicable to provisioned v2 storage accounts. + /// The time the share can be downgraded to lower provisioned IOPs. + /// + public DateTimeOffset? NextAllowedProvisionedIopsDowngradeTime { get; internal set; } + + /// + /// Only applicable to provisioned v2 storage accounts. + /// The time the shaare can be downgraded to lower provisioned bandwidth. + /// + public DateTimeOffset? NextAllowedProvisionedBandwidthDowngradeTime { get; internal set; } + /// /// Internal constructor. /// diff --git a/sdk/storage/Azure.Storage.Files.Shares/src/Models/ShareSetPropertiesOptions.cs b/sdk/storage/Azure.Storage.Files.Shares/src/Models/ShareSetPropertiesOptions.cs index 81dfbf4a5c0be..fd4b4f1b0a061 100644 --- a/sdk/storage/Azure.Storage.Files.Shares/src/Models/ShareSetPropertiesOptions.cs +++ b/sdk/storage/Azure.Storage.Files.Shares/src/Models/ShareSetPropertiesOptions.cs @@ -49,6 +49,20 @@ public class ShareSetPropertiesOptions /// public long? PaidBurstingMaxBandwidthMibps { get; set; } + /// + /// Optional. Supported in version 2025-01-05 and above. Only applicable to provisioned v2 storage accounts. + /// Sets the max provisioned IOPs for a share. For SSD, min IOPs is 3,000 and max is 100,000. + /// For HDD, min IOPs is 500 and max is 50,000. + /// + public long? ProvisionedMaxIops { get; set; } + + /// + /// Optional. Supported in version 2025-01-05 and above. Only applicable to provisioned v2 storage accounts. + /// Sets the max provisioned brandwith for a share. For SSD, min bandwidth is 125 MiB/sec and max is 10,340 MiB/sec. + /// For HDD, min bandwidth is 60 MiB/sec and max is 5,120 MiB/sec. + /// + public long? ProvisionedMaxBandwidthMibps { get; set; } + /// /// Optional to add conditions /// on setting the share's properties. diff --git a/sdk/storage/Azure.Storage.Files.Shares/src/ShareClient.cs b/sdk/storage/Azure.Storage.Files.Shares/src/ShareClient.cs index 7c683888ee9d0..75d4430e5b26d 100644 --- a/sdk/storage/Azure.Storage.Files.Shares/src/ShareClient.cs +++ b/sdk/storage/Azure.Storage.Files.Shares/src/ShareClient.cs @@ -490,6 +490,8 @@ public virtual Response Create( options?.EnablePaidBursting, options?.PaidBurstingMaxIops, options?.PaidBurstingMaxBandwidthMibps, + options?.ProvisionedMaxIops, + options?.ProvisionedMaxBandwidthMibps, async: false, cancellationToken) .EnsureCompleted(); @@ -531,6 +533,8 @@ await CreateInternal( options?.EnablePaidBursting, options?.PaidBurstingMaxIops, options?.PaidBurstingMaxBandwidthMibps, + options?.ProvisionedMaxIops, + options?.ProvisionedMaxBandwidthMibps, async: true, cancellationToken) .ConfigureAwait(false); @@ -577,6 +581,8 @@ public virtual Response Create( enablePaidBursting: default, paidBurstingMaxIops: default, paidBurstingMaxBandwidthMibps: default, + provisionedMaxIops: default, + provisionedMaxBandwidthMibps: default, async: false, cancellationToken) .EnsureCompleted(); @@ -623,6 +629,8 @@ await CreateInternal( enablePaidBursting: default, paidBurstingMaxIops: default, paidBurstingMaxBandwidthMibps: default, + provisionedMaxIops: default, + provisionedMaxBandwidthMibps: default, async: true, cancellationToken) .ConfigureAwait(false); @@ -668,6 +676,12 @@ await CreateInternal( /// Optional. Supported in version 2024-11-04 and above. Only applicable for premium file storage accounts. /// Default if not specified is the maximum throughput the file share can support. Current maximum for a file share is 10,340 MiB/sec. /// + /// + /// Provisioned max IOPS. + /// + /// + /// Provisioned max bandwidth MiBps. + /// /// /// Whether to invoke the operation asynchronously. /// @@ -696,6 +710,8 @@ internal async Task> CreateInternal( bool? enablePaidBursting, long? paidBurstingMaxIops, long? paidBurstingMaxBandwidthMibps, + long? provisionedMaxIops, + long? provisionedMaxBandwidthMibps, bool async, CancellationToken cancellationToken, string operationName = default) @@ -728,6 +744,8 @@ internal async Task> CreateInternal( paidBurstingEnabled: enablePaidBursting, paidBurstingMaxIops: paidBurstingMaxIops, paidBurstingMaxBandwidthMibps: paidBurstingMaxBandwidthMibps, + shareProvisionedIops: provisionedMaxIops, + shareProvisionedBandwidthMibps: provisionedMaxBandwidthMibps, cancellationToken: cancellationToken) .ConfigureAwait(false); } @@ -743,6 +761,8 @@ internal async Task> CreateInternal( paidBurstingEnabled: enablePaidBursting, paidBurstingMaxIops: paidBurstingMaxIops, paidBurstingMaxBandwidthMibps: paidBurstingMaxBandwidthMibps, + shareProvisionedIops: provisionedMaxIops, + shareProvisionedBandwidthMibps: provisionedMaxBandwidthMibps, cancellationToken: cancellationToken); } @@ -791,7 +811,7 @@ internal async Task> CreateInternal( /// a failure occurs. /// public virtual Response CreateIfNotExists( - ShareCreateOptions options, + ShareCreateOptions options = default, CancellationToken cancellationToken = default) => CreateIfNotExistsInternal( options?.Metadata, @@ -803,6 +823,8 @@ public virtual Response CreateIfNotExists( options?.EnablePaidBursting, options?.PaidBurstingMaxIops, options?.PaidBurstingMaxBandwidthMibps, + options?.ProvisionedMaxIops, + options?.ProvisionedMaxBandwidthMibps, async: false, cancellationToken).EnsureCompleted(); @@ -831,7 +853,7 @@ public virtual Response CreateIfNotExists( /// a failure occurs. /// public virtual async Task> CreateIfNotExistsAsync( - ShareCreateOptions options, + ShareCreateOptions options = default, CancellationToken cancellationToken = default) => await CreateIfNotExistsInternal( options?.Metadata, @@ -843,6 +865,8 @@ await CreateIfNotExistsInternal( options?.EnablePaidBursting, options?.PaidBurstingMaxIops, options?.PaidBurstingMaxBandwidthMibps, + options?.ProvisionedMaxIops, + options?.ProvisionedMaxBandwidthMibps, async: true, cancellationToken).ConfigureAwait(false); @@ -874,10 +898,12 @@ await CreateIfNotExistsInternal( /// a failure occurs. /// [EditorBrowsable(EditorBrowsableState.Never)] +#pragma warning disable AZC0002 // DO ensure all service methods, both asynchronous and synchronous, take an optional CancellationToken parameter called cancellationToken. public virtual Response CreateIfNotExists( - Metadata metadata = default, - int? quotaInGB = default, - CancellationToken cancellationToken = default) => +#pragma warning restore AZC0002 // DO ensure all service methods, both asynchronous and synchronous, take an optional CancellationToken parameter called cancellationToken. + Metadata metadata, + int? quotaInGB, + CancellationToken cancellationToken) => CreateIfNotExistsInternal( metadata, quotaInGB, @@ -888,6 +914,8 @@ public virtual Response CreateIfNotExists( enablePaidBursting: default, paidBurstingMaxIops: default, paidBurstingMaxBandwidthMibps: default, + provisionedMaxIops: default, + provisionedMaxBandwidthMibps: default, async: false, cancellationToken).EnsureCompleted(); @@ -918,10 +946,14 @@ public virtual Response CreateIfNotExists( /// A will be thrown if /// a failure occurs. /// + [EditorBrowsable(EditorBrowsableState.Never)] +#pragma warning disable AZC0002 // DO ensure all service methods, both asynchronous and synchronous, take an optional CancellationToken parameter called cancellationToken. public virtual async Task> CreateIfNotExistsAsync( - Metadata metadata = default, - int? quotaInGB = default, - CancellationToken cancellationToken = default) => +#pragma warning restore AZC0002 // DO ensure all service methods, both asynchronous and synchronous, take an optional CancellationToken parameter called cancellationToken. + + Metadata metadata, + int? quotaInGB, + CancellationToken cancellationToken) => await CreateIfNotExistsInternal( metadata, quotaInGB, @@ -932,6 +964,8 @@ await CreateIfNotExistsInternal( enablePaidBursting: default, paidBurstingMaxIops: default, paidBurstingMaxBandwidthMibps: default, + provisionedMaxIops: default, + provisionedMaxBandwidthMibps: default, async: true, cancellationToken).ConfigureAwait(false); @@ -976,6 +1010,12 @@ await CreateIfNotExistsInternal( /// Optional. Supported in version 2024-11-04 and above. Only applicable for premium file storage accounts. /// Default if not specified is the maximum throughput the file share can support. Current maximum for a file share is 10,340 MiB/sec. /// + /// + /// Provisioned max IOPS. + /// + /// + /// Provisioned max bandwidth MiBps. + /// /// /// Whether to invoke the operation asynchronously. /// @@ -1001,6 +1041,8 @@ private async Task> CreateIfNotExistsInternal( bool? enablePaidBursting, long? paidBurstingMaxIops, long? paidBurstingMaxBandwidthMibps, + long? provisionedMaxIops, + long? provisionedMaxBandwidthMibps, bool async, CancellationToken cancellationToken) { @@ -1024,6 +1066,8 @@ private async Task> CreateIfNotExistsInternal( enablePaidBursting, paidBurstingMaxIops, paidBurstingMaxBandwidthMibps, + provisionedMaxIops, + provisionedMaxBandwidthMibps, async, cancellationToken, operationName: $"{nameof(ShareClient)}.{nameof(CreateIfNotExists)}") @@ -1993,6 +2037,8 @@ public virtual Response SetProperties( enablePaidBursting: options?.EnablePaidBursting, paidBurstingMaxIops: options?.PaidBurstingMaxIops, paidBurstingMaxBandwidthMibps: options?.PaidBurstingMaxBandwidthMibps, + provisionedMaxIops: options?.ProvisionedMaxIops, + provisionedMaxBandwidthBandwidthMibps: options?.ProvisionedMaxBandwidthMibps, conditions: options?.Conditions, operationName: $"{nameof(ShareClient)}.{nameof(SetProperties)}", async: false, @@ -2032,6 +2078,8 @@ await SetPropertiesInternal( enablePaidBursting: options?.EnablePaidBursting, paidBurstingMaxIops: options?.PaidBurstingMaxIops, paidBurstingMaxBandwidthMibps: options?.PaidBurstingMaxBandwidthMibps, + provisionedMaxIops: options?.ProvisionedMaxIops, + provisionedMaxBandwidthBandwidthMibps: options?.ProvisionedMaxBandwidthMibps, conditions: options?.Conditions, operationName: $"{nameof(ShareClient)}.{nameof(SetProperties)}", async: true, @@ -2072,6 +2120,16 @@ await SetPropertiesInternal( /// Optional. Supported in version 2024-11-04 and above. Only applicable for premium file storage accounts. /// Default if not specified is the maximum throughput the file share can support. Current maximum for a file share is 10,340 MiB/sec. /// + /// + /// Optional. Supported in version 2025-01-05 and above. Only applicable to provisioned v2 storage accounts. + /// Sets the max provisioned IOPs for a share. For SSD, min IOPs is 3,000 and max is 100,000. + /// For HDD, min IOPs is 500 and max is 50,000. + /// + /// + /// Optional. Supported in version 2025-01-05 and above. Only applicable to provisioned v2 storage accounts. + /// Sets the max provisioned brandwith for a share. For SSD, min bandwidth is 125 MiB/sec and max is 10,340 MiB/sec. + /// For HDD, min bandwidth is 60 MiB/sec and max is 5,120 MiB/sec. + /// /// /// Optional to add conditions /// on setting the quota. @@ -2102,6 +2160,8 @@ internal virtual async Task> SetPropertiesInternal( bool? enablePaidBursting, long? paidBurstingMaxIops, long? paidBurstingMaxBandwidthMibps, + long? provisionedMaxIops, + long? provisionedMaxBandwidthBandwidthMibps, ShareFileRequestConditions conditions, string operationName, bool async, @@ -2134,6 +2194,8 @@ internal virtual async Task> SetPropertiesInternal( paidBurstingEnabled: enablePaidBursting, paidBurstingMaxIops: paidBurstingMaxIops, paidBurstingMaxBandwidthMibps: paidBurstingMaxBandwidthMibps, + shareProvisionedIops: provisionedMaxIops, + shareProvisionedBandwidthMibps: provisionedMaxBandwidthBandwidthMibps, shareFileRequestConditions: conditions, cancellationToken: cancellationToken) .ConfigureAwait(false); @@ -2148,6 +2210,8 @@ internal virtual async Task> SetPropertiesInternal( paidBurstingEnabled: enablePaidBursting, paidBurstingMaxIops: paidBurstingMaxIops, paidBurstingMaxBandwidthMibps: paidBurstingMaxBandwidthMibps, + shareProvisionedIops: provisionedMaxIops, + shareProvisionedBandwidthMibps: provisionedMaxBandwidthBandwidthMibps, shareFileRequestConditions: conditions, cancellationToken: cancellationToken); } @@ -2212,6 +2276,8 @@ public virtual Response SetQuota( enablePaidBursting: default, paidBurstingMaxIops: default, paidBurstingMaxBandwidthMibps: default, + provisionedMaxIops: default, + provisionedMaxBandwidthBandwidthMibps: default, conditions: conditions, operationName: $"{nameof(ShareClient)}.{nameof(SetQuota)}", async: false, @@ -2258,6 +2324,8 @@ await SetPropertiesInternal( enablePaidBursting: default, paidBurstingMaxIops: default, paidBurstingMaxBandwidthMibps: default, + provisionedMaxIops: default, + provisionedMaxBandwidthBandwidthMibps: default, conditions: conditions, operationName: $"{nameof(ShareClient)}.{nameof(SetQuota)}", async: true, @@ -2302,6 +2370,8 @@ public virtual Response SetQuota( enablePaidBursting: default, paidBurstingMaxIops: default, paidBurstingMaxBandwidthMibps: default, + provisionedMaxIops: default, + provisionedMaxBandwidthBandwidthMibps: default, conditions: default, operationName: $"{nameof(ShareClient)}.{nameof(SetQuota)}", async: false, @@ -2345,6 +2415,8 @@ await SetPropertiesInternal( enablePaidBursting: default, paidBurstingMaxIops: default, paidBurstingMaxBandwidthMibps: default, + provisionedMaxIops: default, + provisionedMaxBandwidthBandwidthMibps: default, conditions: default, operationName: $"{nameof(ShareClient)}.{nameof(SetQuota)}", async: true, diff --git a/sdk/storage/Azure.Storage.Files.Shares/src/ShareClientOptions.cs b/sdk/storage/Azure.Storage.Files.Shares/src/ShareClientOptions.cs index 787f0c299080b..30c5ab3b05155 100644 --- a/sdk/storage/Azure.Storage.Files.Shares/src/ShareClientOptions.cs +++ b/sdk/storage/Azure.Storage.Files.Shares/src/ShareClientOptions.cs @@ -148,7 +148,12 @@ public enum ServiceVersion /// /// The 2024-11-04 service version. /// - V2024_11_04 = 24 + V2024_11_04 = 24, + + /// + /// The 2025-01-05 service version. + /// + V2025_01_05 = 25 #pragma warning restore CA1707 // Identifiers should not contain underscores } diff --git a/sdk/storage/Azure.Storage.Files.Shares/src/ShareExtensions.cs b/sdk/storage/Azure.Storage.Files.Shares/src/ShareExtensions.cs index 00f3c357b5746..4d7a0950ab0d6 100644 --- a/sdk/storage/Azure.Storage.Files.Shares/src/ShareExtensions.cs +++ b/sdk/storage/Azure.Storage.Files.Shares/src/ShareExtensions.cs @@ -713,7 +713,11 @@ internal static ShareProperties ToShareProperties(this ResponseWithHeaders StartCopy( metadata: options?.Metadata, smbProperties: options?.SmbProperties, filePermission: options?.FilePermission, + filePermissionFormat: options?.PermissionFormat, filePermissionCopyMode: options?.FilePermissionCopyMode, ignoreReadOnly: options?.IgnoreReadOnly, setArchiveAttribute: options?.Archive, @@ -1378,6 +1379,7 @@ public virtual Response StartCopy( metadata, smbProperties, filePermission, + filePermissionFormat: default, filePermissionCopyMode, ignoreReadOnly, setArchiveAttribute, @@ -1424,6 +1426,7 @@ public virtual Response StartCopy( metadata, smbProperties: default, filePermission: default, + filePermissionFormat: default, filePermissionCopyMode: default, ignoreReadOnly: default, setArchiveAttribute: default, @@ -1467,6 +1470,7 @@ await StartCopyInternal( metadata: options?.Metadata, smbProperties: options?.SmbProperties, filePermission: options?.FilePermission, + filePermissionFormat: options?.PermissionFormat, filePermissionCopyMode: options?.FilePermissionCopyMode, ignoreReadOnly: options?.IgnoreReadOnly, setArchiveAttribute: options?.Archive, @@ -1543,6 +1547,7 @@ await StartCopyInternal( metadata, smbProperties, filePermission, + filePermissionFormat: default, filePermissionCopyMode, ignoreReadOnly, setArchiveAttribute, @@ -1589,6 +1594,7 @@ await StartCopyInternal( metadata, smbProperties: default, filePermission: default, + filePermissionFormat: default, filePermissionCopyMode: default, ignoreReadOnly: default, setArchiveAttribute: default, @@ -1617,6 +1623,9 @@ await StartCopyInternal( /// /// Optional file permission to set for the file. /// + /// + /// Optional file permission format. + /// /// /// Specifies the option to copy file security descriptor from source file or /// to set it using the value which is defined by the header value of FilePermission @@ -1658,6 +1667,7 @@ private async Task> StartCopyInternal( Metadata metadata, FileSmbProperties smbProperties, string filePermission, + FilePermissionFormat? filePermissionFormat, PermissionCopyMode? filePermissionCopyMode, bool? ignoreReadOnly, bool? setArchiveAttribute, @@ -1768,6 +1778,7 @@ private async Task> StartCopyInternal( copySource: uriBuilder.ToString(), metadata: metadata, filePermission: filePermission, + filePermissionFormat: filePermissionFormat, filePermissionKey: smbProperties?.FilePermissionKey, copyFileSmbInfo: copyFileSmbInfo, shareFileRequestConditions: conditions, @@ -1780,6 +1791,7 @@ private async Task> StartCopyInternal( copySource: uriBuilder.ToString(), metadata: metadata, filePermission: filePermission, + filePermissionFormat: filePermissionFormat, filePermissionKey: smbProperties?.FilePermissionKey, copyFileSmbInfo: copyFileSmbInfo, shareFileRequestConditions: conditions, diff --git a/sdk/storage/Azure.Storage.Files.Shares/src/ShareServiceClient.cs b/sdk/storage/Azure.Storage.Files.Shares/src/ShareServiceClient.cs index f6c89f78c214a..b8f3c385d1d22 100644 --- a/sdk/storage/Azure.Storage.Files.Shares/src/ShareServiceClient.cs +++ b/sdk/storage/Azure.Storage.Files.Shares/src/ShareServiceClient.cs @@ -892,6 +892,8 @@ public virtual Response CreateShare( enablePaidBursting: options?.EnablePaidBursting, paidBurstingMaxIops: options?.PaidBurstingMaxIops, paidBurstingMaxBandwidthMibps: options?.PaidBurstingMaxBandwidthMibps, + provisionedMaxIops: options?.PaidBurstingMaxIops, + provisionedMaxBandwidthMibps: options?.ProvisionedMaxBandwidthMibps, async: false, cancellationToken: cancellationToken, operationName: $"{nameof(ShareServiceClient)}.{nameof(CreateShare)}") @@ -944,6 +946,8 @@ public virtual async Task> CreateShareAsync( enablePaidBursting: options?.EnablePaidBursting, paidBurstingMaxIops: options?.PaidBurstingMaxIops, paidBurstingMaxBandwidthMibps: options?.PaidBurstingMaxBandwidthMibps, + provisionedMaxIops: options?.PaidBurstingMaxIops, + provisionedMaxBandwidthMibps: options?.ProvisionedMaxBandwidthMibps, async: true, cancellationToken: cancellationToken, operationName: $"{nameof(ShareServiceClient)}.{nameof(CreateShare)}") @@ -1001,6 +1005,8 @@ public virtual Response CreateShare( enablePaidBursting: default, paidBurstingMaxIops: default, paidBurstingMaxBandwidthMibps: default, + provisionedMaxIops: default, + provisionedMaxBandwidthMibps: default, async: false, cancellationToken: cancellationToken, operationName: $"{nameof(ShareServiceClient)}.{nameof(CreateShare)}") @@ -1058,6 +1064,8 @@ public virtual async Task> CreateShareAsync( enablePaidBursting: default, paidBurstingMaxIops: default, paidBurstingMaxBandwidthMibps: default, + provisionedMaxIops: default, + provisionedMaxBandwidthMibps: default, async: true, cancellationToken: cancellationToken, operationName: $"{nameof(ShareServiceClient)}.{nameof(CreateShare)}") diff --git a/sdk/storage/Azure.Storage.Files.Shares/src/autorest.md b/sdk/storage/Azure.Storage.Files.Shares/src/autorest.md index 43022bc56d1c1..ed634ae302734 100644 --- a/sdk/storage/Azure.Storage.Files.Shares/src/autorest.md +++ b/sdk/storage/Azure.Storage.Files.Shares/src/autorest.md @@ -4,7 +4,7 @@ Run `dotnet build /t:GenerateCode` to generate code. ``` yaml input-file: - - https://raw.githubusercontent.com/Azure/azure-rest-api-specs/98b600498947073c18c2ac5eb7c3c658db5a1a59/specification/storage/data-plane/Microsoft.FileStorage/stable/2024-11-04/file.json + - https://raw.githubusercontent.com/Azure/azure-rest-api-specs/ae95eb6a4701d844bada7d1c4f5ecf4a7444e5b8/specification/storage/data-plane/Microsoft.FileStorage/stable/2025-01-05/file.json generation1-convenience-client: true # https://github.com/Azure/autorest/issues/4075 skip-semantics-validation: true diff --git a/sdk/storage/Azure.Storage.Files.Shares/tests/DisposingShare.cs b/sdk/storage/Azure.Storage.Files.Shares/tests/DisposingShare.cs index 32765a7e04f29..4389023663cf5 100644 --- a/sdk/storage/Azure.Storage.Files.Shares/tests/DisposingShare.cs +++ b/sdk/storage/Azure.Storage.Files.Shares/tests/DisposingShare.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; +using Azure.Storage.Files.Shares.Models; using Azure.Storage.Test.Shared; namespace Azure.Storage.Files.Shares.Tests @@ -16,7 +17,11 @@ public class DisposingShare : IDisposingContainer public static async Task CreateAsync(ShareClient share, IDictionary metadata) { - await share.CreateIfNotExistsAsync(metadata: metadata); + ShareCreateOptions options = new ShareCreateOptions + { + Metadata = metadata + }; + await share.CreateIfNotExistsAsync(options); return new DisposingShare(share); } diff --git a/sdk/storage/Azure.Storage.Files.Shares/tests/FileClientTests.cs b/sdk/storage/Azure.Storage.Files.Shares/tests/FileClientTests.cs index 19bd51da71f56..b1ce4cc25987a 100644 --- a/sdk/storage/Azure.Storage.Files.Shares/tests/FileClientTests.cs +++ b/sdk/storage/Azure.Storage.Files.Shares/tests/FileClientTests.cs @@ -1952,6 +1952,50 @@ await dest.StartCopyAsync( Assert.AreEqual(smbProperties.FileLastWrittenOn, propertiesResponse.Value.SmbProperties.FileLastWrittenOn); } + [RecordedTest] + [TestCase(null)] + [TestCase(FilePermissionFormat.Sddl)] + [TestCase(FilePermissionFormat.Binary)] + [ServiceVersion(Min = ShareClientOptions.ServiceVersion.V2025_01_05)] + public async Task StartCopyAsync_FilePermission_Format(FilePermissionFormat? filePermissionFormat) + { + // Arrange + await using DisposingFile testSource = await SharesClientBuilder.GetTestFileAsync(); + ShareFileClient source = testSource.File; + await using DisposingFile testDest = await SharesClientBuilder.GetTestFileAsync(); + ShareFileClient dest = testSource.File; + + var data = GetRandomBuffer(Constants.KB); + using (var stream = new MemoryStream(data)) + { + await source.UploadRangeAsync( + writeType: ShareFileRangeWriteType.Update, + range: new HttpRange(0, Constants.KB), + content: stream); + } + string filePermission; + if (filePermissionFormat == null || filePermissionFormat == FilePermissionFormat.Sddl) + { + filePermission = "O:S-1-5-21-2127521184-1604012920-1887927527-21560751G:S-1-5-21-2127521184-1604012920-1887927527-513D:AI(A;;FA;;;SY)(A;;FA;;;BA)(A;;0x1200a9;;;S-1-5-21-397955417-626881126-188441444-3053964)S:NO_ACCESS_CONTROL"; + } + else + { + filePermission = "AQAUhGwAAACIAAAAAAAAABQAAAACAFgAAwAAAAAAFAD/AR8AAQEAAAAAAAUSAAAAAAAYAP8BHwABAgAAAAAABSAAAAAgAgAAAAAkAKkAEgABBQAAAAAABRUAAABZUbgXZnJdJWRjOwuMmS4AAQUAAAAAAAUVAAAAoGXPfnhLm1/nfIdwr/1IAQEFAAAAAAAFFQAAAKBlz354S5tf53yHcAECAAA="; + } + + ShareFileCopyOptions options = new ShareFileCopyOptions + { + FilePermission = filePermission, + PermissionFormat = filePermissionFormat, + FilePermissionCopyMode = PermissionCopyMode.Override + }; + + // Act + await dest.StartCopyAsync( + sourceUri: source.Uri, + options: options); + } + [RecordedTest] [ServiceVersion(Min = ShareClientOptions.ServiceVersion.V2021_06_08)] public async Task StartCopyAsync_ChangeTime() diff --git a/sdk/storage/Azure.Storage.Files.Shares/tests/ServiceClientTests.cs b/sdk/storage/Azure.Storage.Files.Shares/tests/ServiceClientTests.cs index 01f071ca1dafb..e52b1b2da1bc6 100644 --- a/sdk/storage/Azure.Storage.Files.Shares/tests/ServiceClientTests.cs +++ b/sdk/storage/Azure.Storage.Files.Shares/tests/ServiceClientTests.cs @@ -431,6 +431,33 @@ public async Task ListSharesSegmentAsync_OAuth() Assert.IsTrue(shares.All(c => c.Properties.Metadata == null)); } + [RecordedTest] + [PlaybackOnly("https://github.com/Azure/azure-sdk-for-net/issues/45675")] + [ServiceVersion(Min = ShareClientOptions.ServiceVersion.V2025_01_05)] + public async Task ListSharesSegmentAsync_ProvisionedBilling() + { + // Arrange + ShareServiceClient service = SharesClientBuilder.GetServiceClient_SharedKey(); + + // Ensure at least one share + await using DisposingShare test = await GetTestShareAsync(service); + ShareClient share = test.Share; + + List shares = new List(); + await foreach (ShareItem item in service.GetSharesAsync()) + { + shares.Add(item); + } + + ShareItem shareItem = shares.FirstOrDefault(); + + // Assert + Assert.IsNotNull(shareItem.Properties.IncludedBurstIops); + Assert.IsNotNull(shareItem.Properties.MaxBurstCreditsForIops); + Assert.IsNotNull(shareItem.Properties.NextAllowedProvisionedIopsDowngradeTime); + Assert.IsNotNull(shareItem.Properties.NextAllowedProvisionedBandwidthDowngradeTime); + } + [RecordedTest] public async Task CreateShareAsync() { diff --git a/sdk/storage/Azure.Storage.Files.Shares/tests/ShareClientTestFixtureAttribute.cs b/sdk/storage/Azure.Storage.Files.Shares/tests/ShareClientTestFixtureAttribute.cs index eb312bb15a31c..7d078484201bc 100644 --- a/sdk/storage/Azure.Storage.Files.Shares/tests/ShareClientTestFixtureAttribute.cs +++ b/sdk/storage/Azure.Storage.Files.Shares/tests/ShareClientTestFixtureAttribute.cs @@ -37,13 +37,14 @@ public ShareClientTestFixtureAttribute(params object[] additionalParameters) ShareClientOptions.ServiceVersion.V2024_05_04, ShareClientOptions.ServiceVersion.V2024_08_04, ShareClientOptions.ServiceVersion.V2024_11_04, + ShareClientOptions.ServiceVersion.V2025_01_05, StorageVersionExtensions.LatestVersion, StorageVersionExtensions.MaxVersion }, additionalParameters: additionalParameters) { - RecordingServiceVersion = StorageVersionExtensions.LatestVersion; - LiveServiceVersions = new object[] { StorageVersionExtensions.MaxVersion, }; + RecordingServiceVersion = StorageVersionExtensions.MaxVersion; + LiveServiceVersions = new object[] { StorageVersionExtensions.LatestVersion, }; } } } diff --git a/sdk/storage/Azure.Storage.Files.Shares/tests/ShareClientTests.cs b/sdk/storage/Azure.Storage.Files.Shares/tests/ShareClientTests.cs index d6655740584fb..f798d99a933a5 100644 --- a/sdk/storage/Azure.Storage.Files.Shares/tests/ShareClientTests.cs +++ b/sdk/storage/Azure.Storage.Files.Shares/tests/ShareClientTests.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Reflection; using System.Threading.Tasks; using Azure.Core.TestFramework; using Azure.Storage.Files.Shares.Models; @@ -13,8 +12,6 @@ using Azure.Storage.Sas; using Azure.Storage.Test; using NUnit.Framework; -using System.Threading; -using Azure.Identity; using Moq; namespace Azure.Storage.Files.Shares.Tests @@ -364,6 +361,57 @@ public async Task CreateAsync_AccessTier() await share.DeleteAsync(); } + [RecordedTest] + [PlaybackOnly("https://github.com/Azure/azure-sdk-for-net/issues/45675")] + [ServiceVersion(Min = ShareClientOptions.ServiceVersion.V2025_01_05)] + public async Task CreateAsync_ProvisionedMaxIopsAndBandwidth() + { + // Arrange + var shareName = GetNewShareName(); + ShareServiceClient service = SharesClientBuilder.GetServiceClient_SharedKey(); + ShareClient share = InstrumentClient(service.GetShareClient(shareName)); + ShareCreateOptions options = new ShareCreateOptions + { + ProvisionedMaxIops = 500, + ProvisionedMaxBandwidthMibps = 125 + }; + + // Act + Response response = await share.CreateAsync(options); + + // Assert + Response propertiesResponse = await share.GetPropertiesAsync(); + Assert.AreEqual(500, propertiesResponse.Value.ProvisionedIops); + Assert.AreEqual(125, propertiesResponse.Value.ProvisionedBandwidthMiBps); + + // Cleanup + await share.DeleteAsync(); + } + + [RecordedTest] + [ServiceVersion(Min = ShareClientOptions.ServiceVersion.V2019_12_12)] + public async Task CreateAsync_AccessTier_Premium() + { + // Arrange + var shareName = GetNewShareName(); + ShareServiceClient service = SharesClientBuilder.GetServiceClient_PremiumFile(); + ShareClient share = InstrumentClient(service.GetShareClient(shareName)); + ShareCreateOptions options = new ShareCreateOptions + { + AccessTier = ShareAccessTier.Premium + }; + + // Act + await share.CreateAsync(options); + + // Assert + Response propertiesResponse = await share.GetPropertiesAsync(); + Assert.AreEqual(ShareAccessTier.Premium.ToString(), propertiesResponse.Value.AccessTier); + + // Cleanup + await share.DeleteAsync(); + } + [RecordedTest] public async Task CreateAsync_Error() { @@ -1774,6 +1822,27 @@ public async Task SetPropertiesAsync_AccessTier() Assert.IsNotNull(response.Value.AccessTierChangeTime); } + [RecordedTest] + [ServiceVersion(Min = ShareClientOptions.ServiceVersion.V2019_12_12)] + public async Task SetPropertiesAsync_AccessTier_Premium() + { + // Arrange + await using DisposingShare test = await GetTestShareAsync(SharesClientBuilder.GetServiceClient_PremiumFile()); + ShareClient share = test.Share; + + ShareSetPropertiesOptions options = new ShareSetPropertiesOptions + { + AccessTier = ShareAccessTier.Premium + }; + + // Act + await share.SetPropertiesAsync(options); + + // Assert + Response response = await share.GetPropertiesAsync(); + Assert.AreEqual(ShareAccessTier.Premium.ToString(), response.Value.AccessTier); + } + [RecordedTest] [PlaybackOnly("https://github.com/Azure/azure-sdk-for-net/issues/17262")] [ServiceVersion(Min = ShareClientOptions.ServiceVersion.V2020_04_08)] @@ -1892,6 +1961,33 @@ public async Task SetPropertiesAsync_PaidBursting() Assert.AreEqual(1000, response.Value.PaidBurstingMaxBandwidthMibps); } + [RecordedTest] + [PlaybackOnly("https://github.com/Azure/azure-sdk-for-net/issues/45675")] + [ServiceVersion(Min = ShareClientOptions.ServiceVersion.V2025_01_05)] + public async Task SetPropertiesAsync_ProvisionedBilling() + { + // Arrange + await using DisposingShare test = await GetTestShareAsync(); + + ShareSetPropertiesOptions setPropertiesOptions = new ShareSetPropertiesOptions + { + ProvisionedMaxIops = 3000, + ProvisionedMaxBandwidthMibps = 125 + }; + + // Act + await test.Share.SetPropertiesAsync(setPropertiesOptions); + + // Assert + Response response = await test.Share.GetPropertiesAsync(); + Assert.AreEqual(3000, response.Value.ProvisionedIops); + Assert.AreEqual(125, response.Value.ProvisionedBandwidthMiBps); + Assert.IsNotNull(response.Value.IncludedBurstIops); + Assert.IsNotNull(response.Value.MaxBurstCreditsForIops); + Assert.IsNotNull(response.Value.NextAllowedProvisionedIopsDowngradeTime); + Assert.IsNotNull(response.Value.NextAllowedProvisionedBandwidthDowngradeTime); + } + [RecordedTest] public async Task SetQuotaAsync() { @@ -1977,7 +2073,11 @@ public async Task DeleteAsync() var shareName = GetNewShareName(); ShareServiceClient service = SharesClientBuilder.GetServiceClient_SharedKey(); ShareClient share = InstrumentClient(service.GetShareClient(shareName)); - await share.CreateIfNotExistsAsync(quotaInGB: 1); + ShareCreateOptions options = new ShareCreateOptions + { + QuotaInGB = 1 + }; + await share.CreateIfNotExistsAsync(options); // Act Response response = await share.DeleteAsync(false); @@ -1993,7 +2093,11 @@ public async Task DeleteAsync_IncludeLeasedSnapshots() // Arrange ShareServiceClient service = SharesClientBuilder.GetServiceClient_SharedKey(); ShareClient share = InstrumentClient(service.GetShareClient(GetNewShareName())); - await share.CreateIfNotExistsAsync(quotaInGB: 1); + ShareCreateOptions createOptions = new ShareCreateOptions + { + QuotaInGB = 1 + }; + await share.CreateIfNotExistsAsync(createOptions); // Create a snapshot Response snapshotResponse0 = await share.CreateSnapshotAsync(); @@ -2141,7 +2245,11 @@ public async Task DeleteAsync_OAuth() var shareName = GetNewShareName(); ShareServiceClient service = GetServiceClient_OAuth(); ShareClient share = InstrumentClient(service.GetShareClient(shareName)); - await share.CreateIfNotExistsAsync(quotaInGB: 1); + ShareCreateOptions options = new ShareCreateOptions + { + QuotaInGB = 1 + }; + await share.CreateIfNotExistsAsync(options); // Act Response response = await share.DeleteAsync(false); diff --git a/sdk/storage/Azure.Storage.Queues/CHANGELOG.md b/sdk/storage/Azure.Storage.Queues/CHANGELOG.md index b53155e9f6dae..dc6bb8705a693 100644 --- a/sdk/storage/Azure.Storage.Queues/CHANGELOG.md +++ b/sdk/storage/Azure.Storage.Queues/CHANGELOG.md @@ -3,12 +3,7 @@ ## 12.21.0-beta.1 (Unreleased) ### Features Added - -### Breaking Changes - -### Bugs Fixed - -### Other Changes +- Added support for service version 2025-01-05. ## 12.20.0 (2024-09-18) diff --git a/sdk/storage/Azure.Storage.Queues/api/Azure.Storage.Queues.net6.0.cs b/sdk/storage/Azure.Storage.Queues/api/Azure.Storage.Queues.net6.0.cs index 3659e69cf1f41..9f440eb3639d7 100644 --- a/sdk/storage/Azure.Storage.Queues/api/Azure.Storage.Queues.net6.0.cs +++ b/sdk/storage/Azure.Storage.Queues/api/Azure.Storage.Queues.net6.0.cs @@ -74,7 +74,7 @@ public QueueClient(System.Uri queueUri, Azure.Storage.StorageSharedKeyCredential } public partial class QueueClientOptions : Azure.Core.ClientOptions { - public QueueClientOptions(Azure.Storage.Queues.QueueClientOptions.ServiceVersion version = Azure.Storage.Queues.QueueClientOptions.ServiceVersion.V2024_11_04) { } + public QueueClientOptions(Azure.Storage.Queues.QueueClientOptions.ServiceVersion version = Azure.Storage.Queues.QueueClientOptions.ServiceVersion.V2025_01_05) { } public Azure.Storage.Queues.Models.QueueAudience? Audience { get { throw null; } set { } } public bool EnableTenantDiscovery { get { throw null; } set { } } public System.Uri GeoRedundantSecondaryUri { get { throw null; } set { } } @@ -107,6 +107,7 @@ public enum ServiceVersion V2024_05_04 = 22, V2024_08_04 = 23, V2024_11_04 = 24, + V2025_01_05 = 25, } } public partial class QueueMessageDecodingFailedEventArgs : Azure.SyncAsyncEventArgs @@ -425,7 +426,7 @@ public event System.EventHandler /// The 2024-11-04 service version. /// - V2024_11_04 = 24 + V2024_11_04 = 24, + + /// + /// The 2025-01-05 service version. + /// + V2025_01_05 = 25 #pragma warning restore CA1707 // Identifiers should not contain underscores } diff --git a/sdk/storage/Azure.Storage.Queues/tests/QueueClientTestFixtureAttribute.cs b/sdk/storage/Azure.Storage.Queues/tests/QueueClientTestFixtureAttribute.cs index 0f2a81dbf9e52..b053e71cdf051 100644 --- a/sdk/storage/Azure.Storage.Queues/tests/QueueClientTestFixtureAttribute.cs +++ b/sdk/storage/Azure.Storage.Queues/tests/QueueClientTestFixtureAttribute.cs @@ -36,6 +36,7 @@ public QueueClientTestFixtureAttribute(params object[] additionalParameters) QueueClientOptions.ServiceVersion.V2024_05_04, QueueClientOptions.ServiceVersion.V2024_08_04, QueueClientOptions.ServiceVersion.V2024_11_04, + QueueClientOptions.ServiceVersion.V2025_01_05, StorageVersionExtensions.LatestVersion, StorageVersionExtensions.MaxVersion },