diff --git a/sdk/storage/azure-storage-blob/CHANGELOG.md b/sdk/storage/azure-storage-blob/CHANGELOG.md index ea92b6022b204..bc3ed7952a3d3 100644 --- a/sdk/storage/azure-storage-blob/CHANGELOG.md +++ b/sdk/storage/azure-storage-blob/CHANGELOG.md @@ -1,6 +1,8 @@ # Release History ## 12.27.0-beta.1 (Unreleased) +- Added support for getting account info on blob container clients and the blob base client. +- Added support for bearer token challenges. ### Features Added diff --git a/sdk/storage/azure-storage-blob/assets.json b/sdk/storage/azure-storage-blob/assets.json index e034c9593e7eb..b3fc915e5e0b2 100644 --- a/sdk/storage/azure-storage-blob/assets.json +++ b/sdk/storage/azure-storage-blob/assets.json @@ -2,5 +2,5 @@ "AssetsRepo": "Azure/azure-sdk-assets", "AssetsRepoPrefixPath": "java", "TagPrefix": "java/storage/azure-storage-blob", - "Tag": "java/storage/azure-storage-blob_bd8a4072b4" + "Tag": "java/storage/azure-storage-blob_e9a4d152f3" } diff --git a/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/BlobContainerAsyncClient.java b/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/BlobContainerAsyncClient.java index 1c0a39cf56f47..e20d45056850f 100644 --- a/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/BlobContainerAsyncClient.java +++ b/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/BlobContainerAsyncClient.java @@ -1520,7 +1520,8 @@ public Mono> getAccountInfoWithResponse() { Mono> getAccountInfoWithResponse(Context context) { context = context == null ? Context.NONE : context; - return this.azureBlobStorage.getContainers().getAccountInfoWithResponseAsync(containerName, context) + return this.azureBlobStorage.getContainers().getAccountInfoWithResponseAsync(containerName, null, + null, context) .map(rb -> { ContainersGetAccountInfoHeaders hd = rb.getDeserializedHeaders(); return new SimpleResponse<>(rb, new StorageAccountInfo(hd.getXMsSkuName(), hd.getXMsAccountKind())); diff --git a/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/BlobServiceAsyncClient.java b/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/BlobServiceAsyncClient.java index ada10bf58f2da..ffd34b33dfe6c 100644 --- a/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/BlobServiceAsyncClient.java +++ b/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/BlobServiceAsyncClient.java @@ -1071,7 +1071,7 @@ public Mono> getAccountInfoWithResponse() { Mono> getAccountInfoWithResponse(Context context) { throwOnAnonymousAccess(); - return this.azureBlobStorage.getServices().getAccountInfoWithResponseAsync(context) + return this.azureBlobStorage.getServices().getAccountInfoWithResponseAsync(null, null, context) .map(rb -> { ServicesGetAccountInfoHeaders hd = rb.getDeserializedHeaders(); return new SimpleResponse<>(rb, new StorageAccountInfo(hd.getXMsSkuName(), hd.getXMsAccountKind(), diff --git a/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/BlobServiceVersion.java b/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/BlobServiceVersion.java index 5ae87c86d2dab..42071556d2025 100644 --- a/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/BlobServiceVersion.java +++ b/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/BlobServiceVersion.java @@ -117,7 +117,12 @@ public enum BlobServiceVersion implements ServiceVersion { /** * Service version {@code 2024-05-04}. */ - V2024_05_04("2024-05-04"); + V2024_05_04("2024-05-04"), + + /** + * Service version {@code 2024-08-04}. + */ + V2024_08_04("2024-08-04"); private final String version; @@ -139,6 +144,6 @@ public String getVersion() { * @return the latest {@link BlobServiceVersion} */ public static BlobServiceVersion getLatest() { - return V2024_05_04; + return V2024_08_04; } } diff --git a/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/implementation/AzureBlobStorageImplBuilder.java b/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/implementation/AzureBlobStorageImplBuilder.java index a3f4c68ffd24f..e1c3405feae06 100644 --- a/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/implementation/AzureBlobStorageImplBuilder.java +++ b/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/implementation/AzureBlobStorageImplBuilder.java @@ -218,7 +218,7 @@ public AzureBlobStorageImplBuilder retryPolicy(RetryPolicy retryPolicy) { @Generated public AzureBlobStorageImpl buildClient() { HttpPipeline localPipeline = (pipeline != null) ? pipeline : createHttpPipeline(); - String localVersion = (version != null) ? version : "2021-12-02"; + String localVersion = (version != null) ? version : "2024-08-04"; SerializerAdapter localSerializerAdapter = (serializerAdapter != null) ? serializerAdapter : JacksonAdapter.createDefaultSerializerAdapter(); AzureBlobStorageImpl client = diff --git a/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/implementation/BlobsImpl.java b/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/implementation/BlobsImpl.java index e712601c59894..f89befa5c39f2 100644 --- a/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/implementation/BlobsImpl.java +++ b/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/implementation/BlobsImpl.java @@ -982,7 +982,9 @@ Mono> getAccountInfo( @PathParam("blob") String blob, @QueryParam("restype") String restype, @QueryParam("comp") String comp, + @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, + @HeaderParam("x-ms-client-request-id") String requestId, @HeaderParam("Accept") String accept, Context context); @@ -995,7 +997,9 @@ Mono> getAccountInfoNoCustomHeaders( @PathParam("blob") String blob, @QueryParam("restype") String restype, @QueryParam("comp") String comp, + @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, + @HeaderParam("x-ms-client-request-id") String requestId, @HeaderParam("Accept") String accept, Context context); @@ -9178,6 +9182,11 @@ public Mono> setTierNoCustomHeadersWithResponseAsync( * * @param containerName The container name. * @param blob The blob name. + * @param timeout 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>. + * @param requestId Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the + * analytics logs when storage analytics logging is enabled. * @throws IllegalArgumentException thrown if parameters fail the validation. * @throws BlobStorageException thrown if the request is rejected by server. * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. @@ -9185,7 +9194,7 @@ public Mono> setTierNoCustomHeadersWithResponseAsync( */ @ServiceMethod(returns = ReturnType.SINGLE) public Mono> getAccountInfoWithResponseAsync( - String containerName, String blob) { + String containerName, String blob, Integer timeout, String requestId) { final String restype = "account"; final String comp = "properties"; final String accept = "application/xml"; @@ -9197,7 +9206,9 @@ public Mono> getAccountInfoWithRe blob, restype, comp, + timeout, this.client.getVersion(), + requestId, accept, context)); } @@ -9207,6 +9218,11 @@ public Mono> getAccountInfoWithRe * * @param containerName The container name. * @param blob The blob name. + * @param timeout 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>. + * @param requestId Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the + * analytics logs when storage analytics logging is enabled. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @throws BlobStorageException thrown if the request is rejected by server. @@ -9215,12 +9231,21 @@ public Mono> getAccountInfoWithRe */ @ServiceMethod(returns = ReturnType.SINGLE) public Mono> getAccountInfoWithResponseAsync( - String containerName, String blob, Context context) { + String containerName, String blob, Integer timeout, String requestId, Context context) { final String restype = "account"; final String comp = "properties"; final String accept = "application/xml"; return service.getAccountInfo( - this.client.getUrl(), containerName, blob, restype, comp, this.client.getVersion(), accept, context); + this.client.getUrl(), + containerName, + blob, + restype, + comp, + timeout, + this.client.getVersion(), + requestId, + accept, + context); } /** @@ -9228,14 +9253,20 @@ public Mono> getAccountInfoWithRe * * @param containerName The container name. * @param blob The blob name. + * @param timeout 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>. + * @param requestId Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the + * analytics logs when storage analytics logging is enabled. * @throws IllegalArgumentException thrown if parameters fail the validation. * @throws BlobStorageException thrown if the request is rejected by server. * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. * @return A {@link Mono} that completes when a successful response is received. */ @ServiceMethod(returns = ReturnType.SINGLE) - public Mono getAccountInfoAsync(String containerName, String blob) { - return getAccountInfoWithResponseAsync(containerName, blob).flatMap(ignored -> Mono.empty()); + public Mono getAccountInfoAsync(String containerName, String blob, Integer timeout, String requestId) { + return getAccountInfoWithResponseAsync(containerName, blob, timeout, requestId) + .flatMap(ignored -> Mono.empty()); } /** @@ -9243,6 +9274,11 @@ public Mono getAccountInfoAsync(String containerName, String blob) { * * @param containerName The container name. * @param blob The blob name. + * @param timeout 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>. + * @param requestId Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the + * analytics logs when storage analytics logging is enabled. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @throws BlobStorageException thrown if the request is rejected by server. @@ -9250,8 +9286,10 @@ public Mono getAccountInfoAsync(String containerName, String blob) { * @return A {@link Mono} that completes when a successful response is received. */ @ServiceMethod(returns = ReturnType.SINGLE) - public Mono getAccountInfoAsync(String containerName, String blob, Context context) { - return getAccountInfoWithResponseAsync(containerName, blob, context).flatMap(ignored -> Mono.empty()); + public Mono getAccountInfoAsync( + String containerName, String blob, Integer timeout, String requestId, Context context) { + return getAccountInfoWithResponseAsync(containerName, blob, timeout, requestId, context) + .flatMap(ignored -> Mono.empty()); } /** @@ -9259,13 +9297,19 @@ public Mono getAccountInfoAsync(String containerName, String blob, Context * * @param containerName The container name. * @param blob The blob name. + * @param timeout 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>. + * @param requestId Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the + * analytics logs when storage analytics logging is enabled. * @throws IllegalArgumentException thrown if parameters fail the validation. * @throws BlobStorageException thrown if the request is rejected by server. * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. * @return the {@link Response} on successful completion of {@link Mono}. */ @ServiceMethod(returns = ReturnType.SINGLE) - public Mono> getAccountInfoNoCustomHeadersWithResponseAsync(String containerName, String blob) { + public Mono> getAccountInfoNoCustomHeadersWithResponseAsync( + String containerName, String blob, Integer timeout, String requestId) { final String restype = "account"; final String comp = "properties"; final String accept = "application/xml"; @@ -9277,7 +9321,9 @@ public Mono> getAccountInfoNoCustomHeadersWithResponseAsync(Strin blob, restype, comp, + timeout, this.client.getVersion(), + requestId, accept, context)); } @@ -9287,6 +9333,11 @@ public Mono> getAccountInfoNoCustomHeadersWithResponseAsync(Strin * * @param containerName The container name. * @param blob The blob name. + * @param timeout 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>. + * @param requestId Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the + * analytics logs when storage analytics logging is enabled. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @throws BlobStorageException thrown if the request is rejected by server. @@ -9295,12 +9346,21 @@ public Mono> getAccountInfoNoCustomHeadersWithResponseAsync(Strin */ @ServiceMethod(returns = ReturnType.SINGLE) public Mono> getAccountInfoNoCustomHeadersWithResponseAsync( - String containerName, String blob, Context context) { + String containerName, String blob, Integer timeout, String requestId, Context context) { final String restype = "account"; final String comp = "properties"; final String accept = "application/xml"; return service.getAccountInfoNoCustomHeaders( - this.client.getUrl(), containerName, blob, restype, comp, this.client.getVersion(), accept, context); + this.client.getUrl(), + containerName, + blob, + restype, + comp, + timeout, + this.client.getVersion(), + requestId, + accept, + context); } /** diff --git a/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/implementation/ContainersImpl.java b/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/implementation/ContainersImpl.java index ddf889773dc4c..ea2cf43655226 100644 --- a/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/implementation/ContainersImpl.java +++ b/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/implementation/ContainersImpl.java @@ -720,7 +720,9 @@ Mono> getAccountInfo( @PathParam("containerName") String containerName, @QueryParam("restype") String restype, @QueryParam("comp") String comp, + @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, + @HeaderParam("x-ms-client-request-id") String requestId, @HeaderParam("Accept") String accept, Context context); @@ -732,7 +734,9 @@ Mono> getAccountInfoNoCustomHeaders( @PathParam("containerName") String containerName, @QueryParam("restype") String restype, @QueryParam("comp") String comp, + @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, + @HeaderParam("x-ms-client-request-id") String requestId, @HeaderParam("Accept") String accept, Context context); } @@ -5817,6 +5821,11 @@ public Mono> listBlobHierarchySegmen * Returns the sku name and account kind. * * @param containerName The container name. + * @param timeout 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>. + * @param requestId Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the + * analytics logs when storage analytics logging is enabled. * @throws IllegalArgumentException thrown if parameters fail the validation. * @throws BlobStorageException thrown if the request is rejected by server. * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. @@ -5824,7 +5833,7 @@ public Mono> listBlobHierarchySegmen */ @ServiceMethod(returns = ReturnType.SINGLE) public Mono> getAccountInfoWithResponseAsync( - String containerName) { + String containerName, Integer timeout, String requestId) { final String restype = "account"; final String comp = "properties"; final String accept = "application/xml"; @@ -5835,7 +5844,9 @@ public Mono> getAccountInfoW containerName, restype, comp, + timeout, this.client.getVersion(), + requestId, accept, context)); } @@ -5844,6 +5855,11 @@ public Mono> getAccountInfoW * Returns the sku name and account kind. * * @param containerName The container name. + * @param timeout 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>. + * @param requestId Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the + * analytics logs when storage analytics logging is enabled. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @throws BlobStorageException thrown if the request is rejected by server. @@ -5852,32 +5868,50 @@ public Mono> getAccountInfoW */ @ServiceMethod(returns = ReturnType.SINGLE) public Mono> getAccountInfoWithResponseAsync( - String containerName, Context context) { + String containerName, Integer timeout, String requestId, Context context) { final String restype = "account"; final String comp = "properties"; final String accept = "application/xml"; return service.getAccountInfo( - this.client.getUrl(), containerName, restype, comp, this.client.getVersion(), accept, context); + this.client.getUrl(), + containerName, + restype, + comp, + timeout, + this.client.getVersion(), + requestId, + accept, + context); } /** * Returns the sku name and account kind. * * @param containerName The container name. + * @param timeout 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>. + * @param requestId Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the + * analytics logs when storage analytics logging is enabled. * @throws IllegalArgumentException thrown if parameters fail the validation. * @throws BlobStorageException thrown if the request is rejected by server. * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. * @return A {@link Mono} that completes when a successful response is received. */ @ServiceMethod(returns = ReturnType.SINGLE) - public Mono getAccountInfoAsync(String containerName) { - return getAccountInfoWithResponseAsync(containerName).flatMap(ignored -> Mono.empty()); + public Mono getAccountInfoAsync(String containerName, Integer timeout, String requestId) { + return getAccountInfoWithResponseAsync(containerName, timeout, requestId).flatMap(ignored -> Mono.empty()); } /** * Returns the sku name and account kind. * * @param containerName The container name. + * @param timeout 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>. + * @param requestId Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the + * analytics logs when storage analytics logging is enabled. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @throws BlobStorageException thrown if the request is rejected by server. @@ -5885,21 +5919,28 @@ public Mono getAccountInfoAsync(String containerName) { * @return A {@link Mono} that completes when a successful response is received. */ @ServiceMethod(returns = ReturnType.SINGLE) - public Mono getAccountInfoAsync(String containerName, Context context) { - return getAccountInfoWithResponseAsync(containerName, context).flatMap(ignored -> Mono.empty()); + public Mono getAccountInfoAsync(String containerName, Integer timeout, String requestId, Context context) { + return getAccountInfoWithResponseAsync(containerName, timeout, requestId, context) + .flatMap(ignored -> Mono.empty()); } /** * Returns the sku name and account kind. * * @param containerName The container name. + * @param timeout 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>. + * @param requestId Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the + * analytics logs when storage analytics logging is enabled. * @throws IllegalArgumentException thrown if parameters fail the validation. * @throws BlobStorageException thrown if the request is rejected by server. * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. * @return the {@link Response} on successful completion of {@link Mono}. */ @ServiceMethod(returns = ReturnType.SINGLE) - public Mono> getAccountInfoNoCustomHeadersWithResponseAsync(String containerName) { + public Mono> getAccountInfoNoCustomHeadersWithResponseAsync( + String containerName, Integer timeout, String requestId) { final String restype = "account"; final String comp = "properties"; final String accept = "application/xml"; @@ -5910,7 +5951,9 @@ public Mono> getAccountInfoNoCustomHeadersWithResponseAsync(Strin containerName, restype, comp, + timeout, this.client.getVersion(), + requestId, accept, context)); } @@ -5919,6 +5962,11 @@ public Mono> getAccountInfoNoCustomHeadersWithResponseAsync(Strin * Returns the sku name and account kind. * * @param containerName The container name. + * @param timeout 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>. + * @param requestId Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the + * analytics logs when storage analytics logging is enabled. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @throws BlobStorageException thrown if the request is rejected by server. @@ -5926,11 +5974,20 @@ public Mono> getAccountInfoNoCustomHeadersWithResponseAsync(Strin * @return the {@link Response} on successful completion of {@link Mono}. */ @ServiceMethod(returns = ReturnType.SINGLE) - public Mono> getAccountInfoNoCustomHeadersWithResponseAsync(String containerName, Context context) { + public Mono> getAccountInfoNoCustomHeadersWithResponseAsync( + String containerName, Integer timeout, String requestId, Context context) { final String restype = "account"; final String comp = "properties"; final String accept = "application/xml"; return service.getAccountInfoNoCustomHeaders( - this.client.getUrl(), containerName, restype, comp, this.client.getVersion(), accept, context); + this.client.getUrl(), + containerName, + restype, + comp, + timeout, + this.client.getVersion(), + requestId, + accept, + context); } } diff --git a/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/implementation/ServicesImpl.java b/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/implementation/ServicesImpl.java index b6012bc4f5a9c..ef8abf7b6c54b 100644 --- a/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/implementation/ServicesImpl.java +++ b/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/implementation/ServicesImpl.java @@ -226,7 +226,9 @@ Mono> getAccountInfo( @HostParam("url") String url, @QueryParam("restype") String restype, @QueryParam("comp") String comp, + @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, + @HeaderParam("x-ms-client-request-id") String requestId, @HeaderParam("Accept") String accept, Context context); @@ -237,7 +239,9 @@ Mono> getAccountInfoNoCustomHeaders( @HostParam("url") String url, @QueryParam("restype") String restype, @QueryParam("comp") String comp, + @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, + @HeaderParam("x-ms-client-request-id") String requestId, @HeaderParam("Accept") String accept, Context context); @@ -1481,24 +1485,43 @@ public Mono> getUserDelegationKeyNoCustomHeadersWith /** * Returns the sku name and account kind. * + * @param timeout 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>. + * @param requestId Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the + * analytics logs when storage analytics logging is enabled. + * @throws IllegalArgumentException thrown if parameters fail the validation. * @throws BlobStorageException thrown if the request is rejected by server. * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. * @return the {@link ResponseBase} on successful completion of {@link Mono}. */ @ServiceMethod(returns = ReturnType.SINGLE) - public Mono> getAccountInfoWithResponseAsync() { + public Mono> getAccountInfoWithResponseAsync( + Integer timeout, String requestId) { final String restype = "account"; final String comp = "properties"; final String accept = "application/xml"; return FluxUtil.withContext( context -> service.getAccountInfo( - this.client.getUrl(), restype, comp, this.client.getVersion(), accept, context)); + this.client.getUrl(), + restype, + comp, + timeout, + this.client.getVersion(), + requestId, + accept, + context)); } /** * Returns the sku name and account kind. * + * @param timeout 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>. + * @param requestId Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the + * analytics logs when storage analytics logging is enabled. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @throws BlobStorageException thrown if the request is rejected by server. @@ -1506,28 +1529,41 @@ public Mono> getAccountInfoWit * @return the {@link ResponseBase} on successful completion of {@link Mono}. */ @ServiceMethod(returns = ReturnType.SINGLE) - public Mono> getAccountInfoWithResponseAsync(Context context) { + public Mono> getAccountInfoWithResponseAsync( + Integer timeout, String requestId, Context context) { final String restype = "account"; final String comp = "properties"; final String accept = "application/xml"; - return service.getAccountInfo(this.client.getUrl(), restype, comp, this.client.getVersion(), accept, context); + return service.getAccountInfo( + this.client.getUrl(), restype, comp, timeout, this.client.getVersion(), requestId, accept, context); } /** * Returns the sku name and account kind. * + * @param timeout 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>. + * @param requestId Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the + * analytics logs when storage analytics logging is enabled. + * @throws IllegalArgumentException thrown if parameters fail the validation. * @throws BlobStorageException thrown if the request is rejected by server. * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. * @return A {@link Mono} that completes when a successful response is received. */ @ServiceMethod(returns = ReturnType.SINGLE) - public Mono getAccountInfoAsync() { - return getAccountInfoWithResponseAsync().flatMap(ignored -> Mono.empty()); + public Mono getAccountInfoAsync(Integer timeout, String requestId) { + return getAccountInfoWithResponseAsync(timeout, requestId).flatMap(ignored -> Mono.empty()); } /** * Returns the sku name and account kind. * + * @param timeout 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>. + * @param requestId Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the + * analytics logs when storage analytics logging is enabled. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @throws BlobStorageException thrown if the request is rejected by server. @@ -1535,31 +1571,49 @@ public Mono getAccountInfoAsync() { * @return A {@link Mono} that completes when a successful response is received. */ @ServiceMethod(returns = ReturnType.SINGLE) - public Mono getAccountInfoAsync(Context context) { - return getAccountInfoWithResponseAsync(context).flatMap(ignored -> Mono.empty()); + public Mono getAccountInfoAsync(Integer timeout, String requestId, Context context) { + return getAccountInfoWithResponseAsync(timeout, requestId, context).flatMap(ignored -> Mono.empty()); } /** * Returns the sku name and account kind. * + * @param timeout 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>. + * @param requestId Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the + * analytics logs when storage analytics logging is enabled. + * @throws IllegalArgumentException thrown if parameters fail the validation. * @throws BlobStorageException thrown if the request is rejected by server. * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. * @return the {@link Response} on successful completion of {@link Mono}. */ @ServiceMethod(returns = ReturnType.SINGLE) - public Mono> getAccountInfoNoCustomHeadersWithResponseAsync() { + public Mono> getAccountInfoNoCustomHeadersWithResponseAsync(Integer timeout, String requestId) { final String restype = "account"; final String comp = "properties"; final String accept = "application/xml"; return FluxUtil.withContext( context -> service.getAccountInfoNoCustomHeaders( - this.client.getUrl(), restype, comp, this.client.getVersion(), accept, context)); + this.client.getUrl(), + restype, + comp, + timeout, + this.client.getVersion(), + requestId, + accept, + context)); } /** * Returns the sku name and account kind. * + * @param timeout 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>. + * @param requestId Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the + * analytics logs when storage analytics logging is enabled. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @throws BlobStorageException thrown if the request is rejected by server. @@ -1567,12 +1621,13 @@ public Mono> getAccountInfoNoCustomHeadersWithResponseAsync() { * @return the {@link Response} on successful completion of {@link Mono}. */ @ServiceMethod(returns = ReturnType.SINGLE) - public Mono> getAccountInfoNoCustomHeadersWithResponseAsync(Context context) { + public Mono> getAccountInfoNoCustomHeadersWithResponseAsync( + Integer timeout, String requestId, Context context) { final String restype = "account"; final String comp = "properties"; final String accept = "application/xml"; return service.getAccountInfoNoCustomHeaders( - this.client.getUrl(), restype, comp, this.client.getVersion(), accept, context); + this.client.getUrl(), restype, comp, timeout, this.client.getVersion(), requestId, accept, context); } /** diff --git a/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/implementation/models/BlobsGetAccountInfoHeaders.java b/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/implementation/models/BlobsGetAccountInfoHeaders.java index 0ace883aa5b21..992e8d2cb9068 100644 --- a/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/implementation/models/BlobsGetAccountInfoHeaders.java +++ b/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/implementation/models/BlobsGetAccountInfoHeaders.java @@ -30,6 +30,12 @@ public final class BlobsGetAccountInfoHeaders { @JsonProperty(value = "x-ms-account-kind") private AccountKind xMsAccountKind; + /* + * The x-ms-is-hns-enabled property. + */ + @JsonProperty(value = "x-ms-is-hns-enabled") + private Boolean xMsIsHnsEnabled; + /* * The x-ms-sku-name property. */ @@ -58,6 +64,8 @@ public final class BlobsGetAccountInfoHeaders { private static final HttpHeaderName X_MS_ACCOUNT_KIND = HttpHeaderName.fromString("x-ms-account-kind"); + private static final HttpHeaderName X_MS_IS_HNS_ENABLED = HttpHeaderName.fromString("x-ms-is-hns-enabled"); + private static final HttpHeaderName X_MS_SKU_NAME = HttpHeaderName.fromString("x-ms-sku-name"); private static final HttpHeaderName X_MS_REQUEST_ID = HttpHeaderName.fromString("x-ms-request-id"); @@ -74,6 +82,10 @@ public BlobsGetAccountInfoHeaders(HttpHeaders rawHeaders) { if (xMsAccountKind != null) { this.xMsAccountKind = AccountKind.fromString(xMsAccountKind); } + String xMsIsHnsEnabled = rawHeaders.getValue(X_MS_IS_HNS_ENABLED); + if (xMsIsHnsEnabled != null) { + this.xMsIsHnsEnabled = Boolean.parseBoolean(xMsIsHnsEnabled); + } String xMsSkuName = rawHeaders.getValue(X_MS_SKU_NAME); if (xMsSkuName != null) { this.xMsSkuName = SkuName.fromString(xMsSkuName); @@ -126,6 +138,26 @@ public BlobsGetAccountInfoHeaders setXMsAccountKind(AccountKind xMsAccountKind) return this; } + /** + * Get the xMsIsHnsEnabled property: The x-ms-is-hns-enabled property. + * + * @return the xMsIsHnsEnabled value. + */ + public Boolean isXMsIsHnsEnabled() { + return this.xMsIsHnsEnabled; + } + + /** + * Set the xMsIsHnsEnabled property: The x-ms-is-hns-enabled property. + * + * @param xMsIsHnsEnabled the xMsIsHnsEnabled value to set. + * @return the BlobsGetAccountInfoHeaders object itself. + */ + public BlobsGetAccountInfoHeaders setXMsIsHnsEnabled(Boolean xMsIsHnsEnabled) { + this.xMsIsHnsEnabled = xMsIsHnsEnabled; + return this; + } + /** * Get the xMsSkuName property: The x-ms-sku-name property. * diff --git a/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/implementation/models/ContainersGetAccountInfoHeaders.java b/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/implementation/models/ContainersGetAccountInfoHeaders.java index c06145a7c9505..81e8f3c0cdbb0 100644 --- a/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/implementation/models/ContainersGetAccountInfoHeaders.java +++ b/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/implementation/models/ContainersGetAccountInfoHeaders.java @@ -30,6 +30,12 @@ public final class ContainersGetAccountInfoHeaders { @JsonProperty(value = "x-ms-account-kind") private AccountKind xMsAccountKind; + /* + * The x-ms-is-hns-enabled property. + */ + @JsonProperty(value = "x-ms-is-hns-enabled") + private Boolean xMsIsHnsEnabled; + /* * The x-ms-sku-name property. */ @@ -58,6 +64,8 @@ public final class ContainersGetAccountInfoHeaders { private static final HttpHeaderName X_MS_ACCOUNT_KIND = HttpHeaderName.fromString("x-ms-account-kind"); + private static final HttpHeaderName X_MS_IS_HNS_ENABLED = HttpHeaderName.fromString("x-ms-is-hns-enabled"); + private static final HttpHeaderName X_MS_SKU_NAME = HttpHeaderName.fromString("x-ms-sku-name"); private static final HttpHeaderName X_MS_REQUEST_ID = HttpHeaderName.fromString("x-ms-request-id"); @@ -74,6 +82,10 @@ public ContainersGetAccountInfoHeaders(HttpHeaders rawHeaders) { if (xMsAccountKind != null) { this.xMsAccountKind = AccountKind.fromString(xMsAccountKind); } + String xMsIsHnsEnabled = rawHeaders.getValue(X_MS_IS_HNS_ENABLED); + if (xMsIsHnsEnabled != null) { + this.xMsIsHnsEnabled = Boolean.parseBoolean(xMsIsHnsEnabled); + } String xMsSkuName = rawHeaders.getValue(X_MS_SKU_NAME); if (xMsSkuName != null) { this.xMsSkuName = SkuName.fromString(xMsSkuName); @@ -126,6 +138,26 @@ public ContainersGetAccountInfoHeaders setXMsAccountKind(AccountKind xMsAccountK return this; } + /** + * Get the xMsIsHnsEnabled property: The x-ms-is-hns-enabled property. + * + * @return the xMsIsHnsEnabled value. + */ + public Boolean isXMsIsHnsEnabled() { + return this.xMsIsHnsEnabled; + } + + /** + * Set the xMsIsHnsEnabled property: The x-ms-is-hns-enabled property. + * + * @param xMsIsHnsEnabled the xMsIsHnsEnabled value to set. + * @return the ContainersGetAccountInfoHeaders object itself. + */ + public ContainersGetAccountInfoHeaders setXMsIsHnsEnabled(Boolean xMsIsHnsEnabled) { + this.xMsIsHnsEnabled = xMsIsHnsEnabled; + return this; + } + /** * Get the xMsSkuName property: The x-ms-sku-name property. * diff --git a/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/implementation/models/StorageError.java b/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/implementation/models/StorageError.java index 3f4f75388fa10..397028f1be1ae 100644 --- a/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/implementation/models/StorageError.java +++ b/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/implementation/models/StorageError.java @@ -18,6 +18,24 @@ public final class StorageError { @JsonProperty(value = "Message") private String message; + /* + * The CopySourceStatusCode property. + */ + @JsonProperty(value = "CopySourceStatusCode") + private Long copySourceStatusCode; + + /* + * The CopySourceErrorCode property. + */ + @JsonProperty(value = "CopySourceErrorCode") + private String copySourceErrorCode; + + /* + * The CopySourceErrorMessage property. + */ + @JsonProperty(value = "CopySourceErrorMessage") + private String copySourceErrorMessage; + /** Creates an instance of StorageError class. */ public StorageError() {} @@ -40,4 +58,64 @@ public StorageError setMessage(String message) { this.message = message; return this; } + + /** + * Get the copySourceStatusCode property: The CopySourceStatusCode property. + * + * @return the copySourceStatusCode value. + */ + public Long getCopySourceStatusCode() { + return this.copySourceStatusCode; + } + + /** + * Set the copySourceStatusCode property: The CopySourceStatusCode property. + * + * @param copySourceStatusCode the copySourceStatusCode value to set. + * @return the StorageError object itself. + */ + public StorageError setCopySourceStatusCode(Long copySourceStatusCode) { + this.copySourceStatusCode = copySourceStatusCode; + return this; + } + + /** + * Get the copySourceErrorCode property: The CopySourceErrorCode property. + * + * @return the copySourceErrorCode value. + */ + public String getCopySourceErrorCode() { + return this.copySourceErrorCode; + } + + /** + * Set the copySourceErrorCode property: The CopySourceErrorCode property. + * + * @param copySourceErrorCode the copySourceErrorCode value to set. + * @return the StorageError object itself. + */ + public StorageError setCopySourceErrorCode(String copySourceErrorCode) { + this.copySourceErrorCode = copySourceErrorCode; + return this; + } + + /** + * Get the copySourceErrorMessage property: The CopySourceErrorMessage property. + * + * @return the copySourceErrorMessage value. + */ + public String getCopySourceErrorMessage() { + return this.copySourceErrorMessage; + } + + /** + * Set the copySourceErrorMessage property: The CopySourceErrorMessage property. + * + * @param copySourceErrorMessage the copySourceErrorMessage value to set. + * @return the StorageError object itself. + */ + public StorageError setCopySourceErrorMessage(String copySourceErrorMessage) { + this.copySourceErrorMessage = copySourceErrorMessage; + return this; + } } diff --git a/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/implementation/util/BuilderHelper.java b/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/implementation/util/BuilderHelper.java index dc7166614bb5d..cf76dc72f4d5c 100644 --- a/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/implementation/util/BuilderHelper.java +++ b/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/implementation/util/BuilderHelper.java @@ -13,7 +13,6 @@ import com.azure.core.http.policy.AddHeadersFromContextPolicy; import com.azure.core.http.policy.AddHeadersPolicy; import com.azure.core.http.policy.AzureSasCredentialPolicy; -import com.azure.core.http.policy.BearerTokenAuthenticationPolicy; import com.azure.core.http.policy.HttpLogOptions; import com.azure.core.http.policy.HttpLoggingPolicy; import com.azure.core.http.policy.HttpPipelinePolicy; @@ -38,6 +37,7 @@ import com.azure.storage.common.policy.RequestRetryOptions; import com.azure.storage.common.policy.ResponseValidationPolicyBuilder; import com.azure.storage.common.policy.ScrubEtagPolicy; +import com.azure.storage.common.policy.StorageBearerTokenChallengeAuthorizationPolicy; import com.azure.storage.common.policy.StorageSharedKeyCredentialPolicy; import java.net.MalformedURLException; @@ -123,7 +123,7 @@ public static HttpPipeline buildPipeline( String scope = audience != null ? ((audience.toString().endsWith("/") ? audience + ".default" : audience + "/.default")) : Constants.STORAGE_SCOPE; - credentialPolicy = new BearerTokenAuthenticationPolicy(tokenCredential, scope); + credentialPolicy = new StorageBearerTokenChallengeAuthorizationPolicy(tokenCredential, scope); } else if (azureSasCredential != null) { credentialPolicy = new AzureSasCredentialPolicy(azureSasCredential, false); } else if (sasToken != null) { diff --git a/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/specialized/BlobAsyncClientBase.java b/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/specialized/BlobAsyncClientBase.java index 05199a63f5b4b..dc1d83a10c14f 100644 --- a/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/specialized/BlobAsyncClientBase.java +++ b/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/specialized/BlobAsyncClientBase.java @@ -2386,7 +2386,8 @@ public Mono> getAccountInfoWithResponse() { } Mono> getAccountInfoWithResponse(Context context) { - return this.azureBlobStorage.getBlobs().getAccountInfoWithResponseAsync(containerName, blobName, context) + return this.azureBlobStorage.getBlobs().getAccountInfoWithResponseAsync(containerName, blobName, null, + null, context) .map(rb -> { BlobsGetAccountInfoHeaders hd = rb.getDeserializedHeaders(); return new SimpleResponse<>(rb, new StorageAccountInfo(hd.getXMsSkuName(), hd.getXMsAccountKind())); diff --git a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobApiTests.java b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobApiTests.java index 01138b5670220..cd40e9cb8d538 100644 --- a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobApiTests.java +++ b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobApiTests.java @@ -2949,6 +2949,30 @@ public void getAccountInfoMin() { assertResponseStatusCode(bc.getAccountInfoWithResponse(null, null), 200); } + @Test + public void getAccountInfoBase() { + StorageAccountInfo info = bc.getAccountInfo(); + + assertNotNull(info.getAccountKind()); + assertNotNull(info.getSkuName()); + assertFalse(info.isHierarchicalNamespaceEnabled()); + } + + @Test + public void getAccountInfoBaseFail() { + BlobServiceClient serviceClient = instrument(new BlobServiceClientBuilder() + .endpoint(ENVIRONMENT.getPrimaryAccount().getBlobEndpoint()) + .credential(new MockTokenCredential())) + .buildClient(); + + BlobClient blobClient = serviceClient.getBlobContainerClient(generateContainerName()).getBlobClient(generateBlobName()); + + BlobStorageException e = assertThrows(BlobStorageException.class, blobClient::getAccountInfo); + assertEquals(BlobErrorCode.INVALID_AUTHENTICATION_INFO, e.getErrorCode()); + + } + + @Test public void getContainerName() { assertEquals(containerName, bc.getContainerName()); @@ -3112,15 +3136,18 @@ public void storageAccountAudience() { assertTrue(aadBlob.exists()); } + @RequiredServiceVersion(clazz = BlobServiceVersion.class, min = "2024-08-04") + @LiveOnly @Test - public void audienceError() { - BlobClient aadBlob = instrument(new BlobClientBuilder().endpoint(bc.getBlobUrl()) - .credential(new MockTokenCredential()) - .audience(BlobAudience.createBlobServiceAccountAudience("badAudience"))) + /* This test tests if the bearer challenge is working properly. A bad audience is passed in, the service returns + the default audience, and the request gets retried with this default audience, making the call function as expected. + */ + public void audienceErrorBearerChallengeRetry() { + BlobClient aadBlob = getBlobClientBuilderWithTokenCredential(bc.getBlobUrl()) + .audience(BlobAudience.createBlobServiceAccountAudience("badAudience")) .buildClient(); - BlobStorageException e = assertThrows(BlobStorageException.class, aadBlob::exists); - assertTrue(e.getErrorCode() == BlobErrorCode.INVALID_AUTHENTICATION_INFO); + assertNotNull(aadBlob.getProperties()); } @Test diff --git a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobAsyncApiTests.java b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobAsyncApiTests.java index d0411848f260c..e6388cafa2af4 100644 --- a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobAsyncApiTests.java +++ b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobAsyncApiTests.java @@ -2637,6 +2637,33 @@ public void getAccountInfoMin() { 200); } + @Test + public void getAccountInfoBase() { + StepVerifier.create(bc.getAccountInfo()) + .assertNext(r -> { + assertNotNull(r.getAccountKind()); + assertNotNull(r.getSkuName()); + assertFalse(r.isHierarchicalNamespaceEnabled()); + }) + .verifyComplete(); + } + + @Test + public void getAccountInfoBaseFail() { + BlobServiceAsyncClient serviceClient = instrument(new BlobServiceClientBuilder() + .endpoint(ENVIRONMENT.getPrimaryAccount().getBlobEndpoint()) + .credential(new MockTokenCredential())) + .buildAsyncClient(); + + BlobAsyncClient blobClient = serviceClient.getBlobContainerAsyncClient(generateContainerName()).getBlobAsyncClient(generateBlobName()); + + StepVerifier.create(blobClient.getAccountInfo()) + .verifyErrorSatisfies(r -> { + BlobStorageException e = assertInstanceOf(BlobStorageException.class, r); + assertEquals(BlobErrorCode.INVALID_AUTHENTICATION_INFO, e.getErrorCode()); + }); + } + @Test public void getContainerName() { assertEquals(containerName, bc.getContainerName()); @@ -2793,18 +2820,20 @@ public void storageAccountAudience() { .verifyComplete(); } + @RequiredServiceVersion(clazz = BlobServiceVersion.class, min = "2024-08-04") + @LiveOnly @Test - public void audienceError() { - BlobAsyncClient aadBlob = instrument(new BlobClientBuilder().endpoint(bc.getBlobUrl()) - .credential(new MockTokenCredential()) - .audience(BlobAudience.createBlobServiceAccountAudience("badAudience"))) + /* This test tests if the bearer challenge is working properly. A bad audience is passed in, the service returns + the default audience, and the request gets retried with this default audience, making the call function as expected. + */ + public void audienceErrorBearerChallengeRetry() { + BlobAsyncClient aadBlob = getBlobClientBuilderWithTokenCredential(bc.getBlobUrl()) + .audience(BlobAudience.createBlobServiceAccountAudience("badAudience")) .buildAsyncClient(); - StepVerifier.create(aadBlob.exists()) - .verifyErrorSatisfies(r -> { - BlobStorageException e = assertInstanceOf(BlobStorageException.class, r); - assertTrue(e.getErrorCode() == BlobErrorCode.INVALID_AUTHENTICATION_INFO); - }); + StepVerifier.create(aadBlob.getProperties()) + .assertNext(r -> assertNotNull(r)) + .verifyComplete(); } @Test diff --git a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/ContainerApiTests.java b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/ContainerApiTests.java index 308d0b7ab93a2..f58a1e4ece3d8 100644 --- a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/ContainerApiTests.java +++ b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/ContainerApiTests.java @@ -45,6 +45,7 @@ import com.azure.storage.common.Utility; import com.azure.storage.common.implementation.Constants; import com.azure.storage.common.test.shared.TestHttpClientType; +import com.azure.storage.common.test.shared.extensions.LiveOnly; import com.azure.storage.common.test.shared.extensions.PlaybackOnly; import com.azure.storage.common.test.shared.extensions.RequiredServiceVersion; import org.junit.jupiter.api.BeforeEach; @@ -1896,6 +1897,30 @@ public void getAccountInfoMin() { assertResponseStatusCode(primaryBlobServiceClient.getAccountInfoWithResponse(null, null), 200); } + @Test + public void getAccountInfoBase() { + cc = primaryBlobServiceClient.getBlobContainerClient(generateContainerName()); + StorageAccountInfo info = cc.getAccountInfo(null); + + assertNotNull(info.getAccountKind()); + assertNotNull(info.getSkuName()); + assertFalse(info.isHierarchicalNamespaceEnabled()); + } + + @Test + public void getAccountInfoBaseFail() { + BlobServiceClient serviceClient = instrument(new BlobServiceClientBuilder() + .endpoint(ENVIRONMENT.getPrimaryAccount().getBlobEndpoint()) + .credential(new MockTokenCredential())) + .buildClient(); + + BlobContainerClient containerClient = serviceClient.getBlobContainerClient(generateContainerName()); + + BlobStorageException e = assertThrows(BlobStorageException.class, () -> containerClient.getAccountInfo(null)); + assertEquals(BlobErrorCode.INVALID_AUTHENTICATION_INFO, e.getErrorCode()); + + } + @Test public void getContainerName() { String containerName = generateContainerName(); @@ -1954,15 +1979,18 @@ public void storageAccountAudience() { assertTrue(aadContainer.exists()); } + @RequiredServiceVersion(clazz = BlobServiceVersion.class, min = "2024-08-04") + @LiveOnly @Test - public void audienceError() { - BlobContainerClient aadContainer = getContainerClientBuilder(cc.getBlobContainerUrl()) - .credential(new MockTokenCredential()) - .audience(BlobAudience.createBlobServiceAccountAudience("badAudience")) - .buildClient(); + /* This test tests if the bearer challenge is working properly. A bad audience is passed in, the service returns + the default audience, and the request gets retried with this default audience, making the call function as expected. + */ + public void audienceErrorBearerChallengeRetry() { + BlobContainerClient aadContainer = getContainerClientBuilderWithTokenCredential(cc.getBlobContainerUrl()) + .audience(BlobAudience.createBlobServiceAccountAudience("badAudience")) + .buildClient(); - BlobStorageException e = assertThrows(BlobStorageException.class, () -> aadContainer.exists()); - assertTrue(e.getErrorCode() == BlobErrorCode.INVALID_AUTHENTICATION_INFO); + assertNotNull(aadContainer.getProperties()); } @Test diff --git a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/ContainerAsyncApiTests.java b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/ContainerAsyncApiTests.java index 6923c54c49205..bc56b852797c8 100644 --- a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/ContainerAsyncApiTests.java +++ b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/ContainerAsyncApiTests.java @@ -41,6 +41,7 @@ import com.azure.storage.blob.specialized.PageBlobAsyncClient; import com.azure.storage.common.implementation.Constants; import com.azure.storage.common.test.shared.TestHttpClientType; +import com.azure.storage.common.test.shared.extensions.LiveOnly; import com.azure.storage.common.test.shared.extensions.PlaybackOnly; import com.azure.storage.common.test.shared.extensions.RequiredServiceVersion; import org.junit.jupiter.api.BeforeEach; @@ -2011,6 +2012,35 @@ public void getAccountInfoMin() { assertAsyncResponseStatusCode(primaryBlobServiceAsyncClient.getAccountInfoWithResponse(), 200); } + @Test + public void getAccountInfoBase() { + ccAsync = primaryBlobServiceAsyncClient.getBlobContainerAsyncClient(generateContainerName()); + + StepVerifier.create(ccAsync.getAccountInfo()) + .assertNext(r -> { + assertNotNull(r.getAccountKind()); + assertNotNull(r.getSkuName()); + assertFalse(r.isHierarchicalNamespaceEnabled()); + }) + .verifyComplete(); + } + + @Test + public void getAccountInfoBaseFail() { + BlobServiceAsyncClient serviceClient = instrument(new BlobServiceClientBuilder() + .endpoint(ENVIRONMENT.getPrimaryAccount().getBlobEndpoint()) + .credential(new MockTokenCredential())) + .buildAsyncClient(); + + BlobContainerAsyncClient containerClient = serviceClient.getBlobContainerAsyncClient(generateContainerName()); + + StepVerifier.create(containerClient.getAccountInfo()) + .verifyErrorSatisfies(r -> { + BlobStorageException e = assertInstanceOf(BlobStorageException.class, r); + assertEquals(BlobErrorCode.INVALID_AUTHENTICATION_INFO, e.getErrorCode()); + }); + } + @Test public void getContainerName() { String containerName = generateContainerName(); @@ -2072,18 +2102,20 @@ public void storageAccountAudience() { .expectNext(true); } + @RequiredServiceVersion(clazz = BlobServiceVersion.class, min = "2024-08-04") + @LiveOnly @Test - public void audienceError() { - BlobContainerAsyncClient aadContainer = getContainerClientBuilder(ccAsync.getBlobContainerUrl()) - .credential(new MockTokenCredential()) - .audience(BlobAudience.createBlobServiceAccountAudience("badAudience")) - .buildAsyncClient(); + /* This test tests if the bearer challenge is working properly. A bad audience is passed in, the service returns + the default audience, and the request gets retried with this default audience, making the call function as expected. + */ + public void audienceErrorBearerChallengeRetry() { + BlobContainerAsyncClient aadContainer = getContainerClientBuilderWithTokenCredential(ccAsync.getBlobContainerUrl()) + .audience(BlobAudience.createBlobServiceAccountAudience("badAudience")) + .buildAsyncClient(); StepVerifier.create(aadContainer.exists()) - .verifyErrorSatisfies(r -> { - BlobStorageException e = assertInstanceOf(BlobStorageException.class, r); - assertTrue(e.getErrorCode() == BlobErrorCode.INVALID_AUTHENTICATION_INFO); - }); + .assertNext(r -> assertNotNull(r)) + .verifyComplete(); } @Test diff --git a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/ServiceApiTests.java b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/ServiceApiTests.java index 013b7c066c98a..a2eca961da1c1 100644 --- a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/ServiceApiTests.java +++ b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/ServiceApiTests.java @@ -8,7 +8,6 @@ import com.azure.core.http.rest.PagedIterable; import com.azure.core.http.rest.PagedResponse; import com.azure.core.http.rest.Response; -import com.azure.core.test.utils.MockTokenCredential; import com.azure.core.util.Context; import com.azure.core.util.paging.ContinuablePage; import com.azure.identity.DefaultAzureCredentialBuilder; @@ -1141,16 +1140,18 @@ public void storageAccountAudience() { assertNotNull(aadService.getProperties()); } + @RequiredServiceVersion(clazz = BlobServiceVersion.class, min = "2024-08-04") + @LiveOnly @Test - public void audienceError() { - BlobServiceClient aadService = instrument(new BlobServiceClientBuilder() - .endpoint(cc.getBlobContainerUrl()) - .credential(new MockTokenCredential()) - .audience(BlobAudience.createBlobServiceAccountAudience("badAudience"))) - .buildClient(); + /* This test tests if the bearer challenge is working properly. A bad audience is passed in, the service returns + the default audience, and the request gets retried with this default audience, making the call function as expected. + */ + public void audienceErrorBearerChallengeRetry() { + BlobServiceClient aadService = getServiceClientBuilderWithTokenCredential(cc.getBlobContainerUrl()) + .audience(BlobAudience.createBlobServiceAccountAudience("badAudience")) + .buildClient(); - BlobStorageException e = assertThrows(BlobStorageException.class, () -> aadService.getProperties()); - assertTrue(e.getErrorCode() == BlobErrorCode.INVALID_AUTHENTICATION_INFO); + assertNotNull(aadService.getProperties()); } @Test diff --git a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/ServiceAsyncApiTests.java b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/ServiceAsyncApiTests.java index d4fd89589f128..840219785db51 100644 --- a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/ServiceAsyncApiTests.java +++ b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/ServiceAsyncApiTests.java @@ -7,7 +7,6 @@ import com.azure.core.http.rest.PagedResponse; import com.azure.core.http.rest.Response; import com.azure.core.test.TestMode; -import com.azure.core.test.utils.MockTokenCredential; import com.azure.core.util.Context; import com.azure.core.util.paging.ContinuablePage; import com.azure.identity.DefaultAzureCredentialBuilder; @@ -1204,19 +1203,20 @@ public void storageAccountAudience() { .verifyComplete(); } + @RequiredServiceVersion(clazz = BlobServiceVersion.class, min = "2024-08-04") + @LiveOnly @Test - public void audienceError() { - BlobServiceAsyncClient aadService = instrument(new BlobServiceClientBuilder() - .endpoint(ccAsync.getBlobContainerUrl()) - .credential(new MockTokenCredential()) - .audience(BlobAudience.createBlobServiceAccountAudience("badAudience"))) - .buildAsyncClient(); + /* This test tests if the bearer challenge is working properly. A bad audience is passed in, the service returns + the default audience, and the request gets retried with this default audience, making the call function as expected. + */ + public void audienceErrorBearerChallengeRetry() { + BlobServiceAsyncClient aadService = getServiceClientBuilderWithTokenCredential(ccAsync.getBlobContainerUrl()) + .audience(BlobAudience.createBlobServiceAccountAudience("badAudience")) + .buildAsyncClient(); StepVerifier.create(aadService.getProperties()) - .verifyErrorSatisfies(r -> { - BlobStorageException e = assertInstanceOf(BlobStorageException.class, r); - assertTrue(e.getErrorCode() == BlobErrorCode.INVALID_AUTHENTICATION_INFO); - }); + .assertNext(r -> assertNotNull(r)) + .verifyComplete(); } @Test diff --git a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/specialized/AppendBlobApiTests.java b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/specialized/AppendBlobApiTests.java index 59376401de1f2..b3285d4ea50a7 100644 --- a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/specialized/AppendBlobApiTests.java +++ b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/specialized/AppendBlobApiTests.java @@ -5,7 +5,6 @@ import com.azure.core.exception.UnexpectedLengthException; import com.azure.core.http.rest.Response; -import com.azure.core.test.utils.MockTokenCredential; import com.azure.core.test.utils.TestUtils; import com.azure.core.util.Context; import com.azure.storage.blob.BlobServiceVersion; @@ -25,6 +24,7 @@ import com.azure.storage.blob.sas.BlobContainerSasPermission; import com.azure.storage.blob.sas.BlobServiceSasSignatureValues; import com.azure.storage.common.implementation.Constants; +import com.azure.storage.common.test.shared.extensions.LiveOnly; import com.azure.storage.common.test.shared.extensions.RequiredServiceVersion; import com.azure.storage.common.test.shared.policy.TransientFailureInjectingHttpPipelinePolicy; import org.junit.jupiter.api.BeforeEach; @@ -530,6 +530,19 @@ public void appendBlockFromURLMin() { validateBasicHeaders(response.getHeaders()); } + /*@RequiredServiceVersion(clazz = BlobServiceVersion.class, min = "2024-08-04") + @Test + public void appendBlockFromURLSourceErrorAndStatusCodeNewTest() { + AppendBlobClient destBlob = cc.getBlobClient(generateBlobName()).getAppendBlobClient(); + destBlob.createIfNotExists(); + + BlobStorageException e = assertThrows(BlobStorageException.class, () -> destBlob.appendBlockFromUrl(bc.getBlobUrl(), new BlobRange(0, (long) PageBlobClient.PAGE_BYTES))); + + assertTrue(e.getStatusCode() == 409); + assertTrue(e.getServiceMessage().contains("PublicAccessNotPermitted")); + assertTrue(e.getServiceMessage().contains("Public access is not permitted on this storage account.")); + }*/ + @Test public void appendBlockFromURLRange() { byte[] data = getRandomByteArray(4 * 1024); @@ -838,16 +851,18 @@ public void storageAccountAudience() { assertTrue(aadBlob.exists()); } + @RequiredServiceVersion(clazz = BlobServiceVersion.class, min = "2024-08-04") + @LiveOnly @Test - public void audienceError() { - AppendBlobClient aadBlob = instrument(new SpecializedBlobClientBuilder() - .endpoint(bc.getBlobUrl()) - .credential(new MockTokenCredential()) - .audience(BlobAudience.createBlobServiceAccountAudience("badAudience"))) + /* This test tests if the bearer challenge is working properly. A bad audience is passed in, the service returns + the default audience, and the request gets retried with this default audience, making the call function as expected. + */ + public void audienceErrorBearerChallengeRetry() { + AppendBlobClient aadBlob = getSpecializedBuilderWithTokenCredential(bc.getBlobUrl()) + .audience(BlobAudience.createBlobServiceAccountAudience("badAudience")) .buildAppendBlobClient(); - BlobStorageException e = assertThrows(BlobStorageException.class, () -> aadBlob.exists()); - assertTrue(e.getErrorCode() == BlobErrorCode.INVALID_AUTHENTICATION_INFO); + assertNotNull(aadBlob.getProperties()); } @Test diff --git a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/specialized/AppendBlobAsyncApiTests.java b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/specialized/AppendBlobAsyncApiTests.java index e612940a2d55a..5235280646e23 100644 --- a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/specialized/AppendBlobAsyncApiTests.java +++ b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/specialized/AppendBlobAsyncApiTests.java @@ -4,7 +4,6 @@ package com.azure.storage.blob.specialized; import com.azure.core.exception.UnexpectedLengthException; -import com.azure.core.test.utils.MockTokenCredential; import com.azure.core.test.utils.TestUtils; import com.azure.core.util.FluxUtil; import com.azure.storage.blob.BlobServiceVersion; @@ -22,6 +21,7 @@ import com.azure.storage.blob.sas.BlobSasPermission; import com.azure.storage.blob.sas.BlobServiceSasSignatureValues; import com.azure.storage.common.implementation.Constants; +import com.azure.storage.common.test.shared.extensions.LiveOnly; import com.azure.storage.common.test.shared.extensions.RequiredServiceVersion; import com.azure.storage.common.test.shared.policy.TransientFailureInjectingHttpPipelinePolicy; import org.junit.jupiter.api.BeforeEach; @@ -558,6 +558,20 @@ public void appendBlockFromURLMin() { .verifyComplete(); } + /*@RequiredServiceVersion(clazz = BlobServiceVersion.class, min = "2024-08-04") + @Test + public void appendBlockFromURLSourceErrorAndStatusCodeNewTest() { + AppendBlobAsyncClient destBlob = ccAsync.getBlobAsyncClient(generateBlobName()).getAppendBlobAsyncClient(); + + StepVerifier.create(destBlob.createIfNotExists().then(destBlob.appendBlockFromUrl(bc.getBlobUrl(), new BlobRange(0, (long) PageBlobClient.PAGE_BYTES)))) + .verifyErrorSatisfies(r -> { + BlobStorageException e = assertInstanceOf(BlobStorageException.class, r); + assertTrue(e.getStatusCode() == 409); + assertTrue(e.getServiceMessage().contains("PublicAccessNotPermitted")); + assertTrue(e.getServiceMessage().contains("Public access is not permitted on this storage account.")); + }); + }*/ + @Test public void appendBlockFromURLRange() { byte[] data = getRandomByteArray(4 * 1024); @@ -890,19 +904,20 @@ public void storageAccountAudience() { .verifyComplete(); } + @RequiredServiceVersion(clazz = BlobServiceVersion.class, min = "2024-08-04") + @LiveOnly @Test - public void audienceError() { - AppendBlobAsyncClient aadBlob = instrument(new SpecializedBlobClientBuilder() - .endpoint(bc.getBlobUrl()) - .credential(new MockTokenCredential()) - .audience(BlobAudience.createBlobServiceAccountAudience("badAudience"))) + /* This test tests if the bearer challenge is working properly. A bad audience is passed in, the service returns + the default audience, and the request gets retried with this default audience, making the call function as expected. + */ + public void audienceErrorBearerChallengeRetry() { + AppendBlobAsyncClient aadBlob = getSpecializedBuilderWithTokenCredential(bc.getBlobUrl()) + .audience(BlobAudience.createBlobServiceAccountAudience("badAudience")) .buildAppendBlobAsyncClient(); - StepVerifier.create(aadBlob.exists()) - .verifyErrorSatisfies(r -> { - BlobStorageException e = assertInstanceOf(BlobStorageException.class, r); - assertTrue(e.getErrorCode() == BlobErrorCode.INVALID_AUTHENTICATION_INFO); - }); + StepVerifier.create(aadBlob.getProperties()) + .assertNext(r -> assertNotNull(r)) + .verifyComplete(); } @Test diff --git a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/specialized/BlobBaseApiTests.java b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/specialized/BlobBaseApiTests.java index 1ee8e9b9df1aa..5a512b5252cfd 100644 --- a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/specialized/BlobBaseApiTests.java +++ b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/specialized/BlobBaseApiTests.java @@ -812,6 +812,18 @@ public void queryACFail(OffsetDateTime modified, OffsetDateTime unmodified, Stri () -> bc.queryWithResponse(optionsOs, null, null)); } + /*@RequiredServiceVersion(clazz = BlobServiceVersion.class, min = "2024-08-04") + @Test + public void copyFromURLSourceErrorAndStatusCode() { + BlockBlobClient destBlob = cc.getBlobClient(generateBlobName()).getBlockBlobClient(); + + BlobStorageException e = assertThrows(BlobStorageException.class, () -> destBlob.copyFromUrl(bc.getBlobUrl())); + + assertTrue(e.getStatusCode() == 409); + assertTrue(e.getServiceMessage().contains("PublicAccessNotPermitted")); + assertTrue(e.getServiceMessage().contains("Public access is not permitted on this storage account.")); + }*/ + static class MockProgressConsumer implements Consumer { List progressList; diff --git a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/specialized/BlobBaseAsyncApiTests.java b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/specialized/BlobBaseAsyncApiTests.java index 3d4ba056cb0d2..b9ec8cc3cf97b 100644 --- a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/specialized/BlobBaseAsyncApiTests.java +++ b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/specialized/BlobBaseAsyncApiTests.java @@ -584,6 +584,21 @@ public void queryACFail(OffsetDateTime modified, OffsetDateTime unmodified, Stri .verifyError(BlobStorageException.class); } + /*@RequiredServiceVersion(clazz = BlobServiceVersion.class, min = "2024-08-04") + @Test + public void copyFromURLSourceErrorAndStatusCode() { + BlockBlobAsyncClient destBlob = ccAsync.getBlobAsyncClient(generateBlobName()).getBlockBlobAsyncClient(); + + StepVerifier.create(destBlob.copyFromUrl(bc.getBlobUrl())) + .verifyErrorSatisfies(r -> { + BlobStorageException e = assertInstanceOf(BlobStorageException.class, r); + assertTrue(e.getStatusCode() == 409); + assertTrue(e.getServiceMessage().contains("PublicAccessNotPermitted")); + assertTrue(e.getServiceMessage().contains("Public access is not permitted on this storage account.")); + }); + }*/ + + static class MockProgressConsumer implements Consumer { List progressList; diff --git a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/specialized/BlockBlobApiTests.java b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/specialized/BlockBlobApiTests.java index 934ca026a1760..1cfbff4aa819d 100644 --- a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/specialized/BlockBlobApiTests.java +++ b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/specialized/BlockBlobApiTests.java @@ -7,7 +7,6 @@ import com.azure.core.http.HttpHeaderName; import com.azure.core.http.HttpHeaders; import com.azure.core.http.rest.Response; -import com.azure.core.test.utils.MockTokenCredential; import com.azure.core.test.utils.TestUtils; import com.azure.core.util.BinaryData; import com.azure.core.util.Context; @@ -376,6 +375,20 @@ public void stageBlockFromUrl() { assertEquals(ByteBuffer.wrap(outputStream.toByteArray()), DATA.getDefaultData()); } + /*@RequiredServiceVersion(clazz = BlobServiceVersion.class, min = "2024-08-04") + @Test + public void stageBlockFromUrlSourceErrorAndStatusCode() { + BlockBlobClient destBlob = cc.getBlobClient(generateBlobName()).getBlockBlobClient(); + + String blockID = getBlockID(); + + BlobStorageException e = assertThrows(BlobStorageException.class, () -> destBlob.stageBlockFromUrl(blockID, blockBlobClient.getBlobUrl(), new BlobRange(0, (long) PageBlobClient.PAGE_BYTES))); + + assertTrue(e.getStatusCode() == 409); + assertTrue(e.getServiceMessage().contains("PublicAccessNotPermitted")); + assertTrue(e.getServiceMessage().contains("Public access is not permitted on this storage account.")); + }*/ + @Test public void stageBlockFromUrlMin() { BlockBlobClient bu2 = cc.getBlobClient(generateBlobName()).getBlockBlobClient(); @@ -1485,6 +1498,18 @@ public void uploadFromUrlMin() { TestUtils.assertArraysEqual(DATA.getDefaultBytes(), os.toByteArray()); } + /*@RequiredServiceVersion(clazz = BlobServiceVersion.class, min = "2024-08-04") + @Test + public void uploadFromUrlSourceErrorAndStatusCode() { + BlockBlobClient destBlob = cc.getBlobClient(generateBlobName()).getBlockBlobClient(); + + BlobStorageException e = assertThrows(BlobStorageException.class, () -> destBlob.uploadFromUrl(blockBlobClient.getBlobUrl())); + + assertTrue(e.getStatusCode() == 409); + assertTrue(e.getServiceMessage().contains("PublicAccessNotPermitted")); + assertTrue(e.getServiceMessage().contains("Public access is not permitted on this storage account.")); + }*/ + @RequiredServiceVersion(clazz = BlobServiceVersion.class, min = "2020-04-08") @Test public void uploadFromUrlOverwrite() { @@ -1741,16 +1766,18 @@ public void storageAccountAudience() { assertTrue(aadBlob.exists()); } + @RequiredServiceVersion(clazz = BlobServiceVersion.class, min = "2024-08-04") + @LiveOnly @Test - public void audienceError() { - BlockBlobClient aadBlob = instrument(new SpecializedBlobClientBuilder() - .endpoint(blockBlobClient.getBlobUrl()) - .credential(new MockTokenCredential()) - .audience(BlobAudience.createBlobServiceAccountAudience("badAudience"))) + /* This test tests if the bearer challenge is working properly. A bad audience is passed in, the service returns + the default audience, and the request gets retried with this default audience, making the call function as expected. + */ + public void audienceErrorBearerChallengeRetry() { + BlockBlobClient aadBlob = getSpecializedBuilderWithTokenCredential(blockBlobClient.getBlobUrl()) + .audience(BlobAudience.createBlobServiceAccountAudience("badAudience")) .buildBlockBlobClient(); - BlobStorageException e = assertThrows(BlobStorageException.class, () -> aadBlob.exists()); - assertTrue(e.getErrorCode() == BlobErrorCode.INVALID_AUTHENTICATION_INFO); + assertNotNull(aadBlob.getProperties()); } @Test diff --git a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/specialized/BlockBlobAsyncApiTests.java b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/specialized/BlockBlobAsyncApiTests.java index e28a755f3f125..c9d387549edf6 100644 --- a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/specialized/BlockBlobAsyncApiTests.java +++ b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/specialized/BlockBlobAsyncApiTests.java @@ -12,7 +12,6 @@ import com.azure.core.http.policy.HttpPipelinePolicy; import com.azure.core.http.rest.Response; import com.azure.core.test.http.MockHttpResponse; -import com.azure.core.test.utils.MockTokenCredential; import com.azure.core.test.utils.TestUtils; import com.azure.core.util.BinaryData; import com.azure.core.util.FluxUtil; @@ -422,6 +421,22 @@ public void stageBlockFromUrl() { .verifyComplete(); } + /*@RequiredServiceVersion(clazz = BlobServiceVersion.class, min = "2024-08-04") + @Test + public void stageBlockFromUrlSourceErrorAndStatusCode() { + BlockBlobAsyncClient destBlob = ccAsync.getBlobAsyncClient(generateBlobName()).getBlockBlobAsyncClient(); + + String blockID = getBlockID(); + + StepVerifier.create(destBlob.stageBlockFromUrl(blockID, blockBlobAsyncClient.getBlobUrl(), new BlobRange(0, (long) PageBlobClient.PAGE_BYTES))) + .verifyErrorSatisfies(r -> { + BlobStorageException e = assertInstanceOf(BlobStorageException.class, r); + assertTrue(e.getStatusCode() == 409); + assertTrue(e.getServiceMessage().contains("PublicAccessNotPermitted")); + assertTrue(e.getServiceMessage().contains("Public access is not permitted on this storage account.")); + }); + }*/ + @Test public void stageBlockFromUrlMin() { BlockBlobAsyncClient bu2 = ccAsync.getBlobAsyncClient(generateBlobName()).getBlockBlobAsyncClient(); @@ -2353,6 +2368,20 @@ public void uploadFromUrlMin() { .verifyComplete(); } + /*@RequiredServiceVersion(clazz = BlobServiceVersion.class, min = "2024-08-04") + @Test + public void uploadFromUrlSourceErrorAndStatusCode() { + BlockBlobAsyncClient destBlob = ccAsync.getBlobAsyncClient(generateBlobName()).getBlockBlobAsyncClient(); + + StepVerifier.create(destBlob.uploadFromUrl(blockBlobAsyncClient.getBlobUrl())) + .verifyErrorSatisfies(r -> { + BlobStorageException e = assertInstanceOf(BlobStorageException.class, r); + assertTrue(e.getStatusCode() == 409); + assertTrue(e.getServiceMessage().contains("PublicAccessNotPermitted")); + assertTrue(e.getServiceMessage().contains("Public access is not permitted on this storage account.")); + }); + }*/ + @RequiredServiceVersion(clazz = BlobServiceVersion.class, min = "2020-04-08") @Test public void uploadFromUrlOverwrite() { @@ -2620,19 +2649,20 @@ public void storageAccountAudience() { .verifyComplete(); } + @RequiredServiceVersion(clazz = BlobServiceVersion.class, min = "2024-08-04") + @LiveOnly @Test - public void audienceError() { - BlockBlobAsyncClient aadBlob = instrument(new SpecializedBlobClientBuilder() - .endpoint(blockBlobAsyncClient.getBlobUrl()) - .credential(new MockTokenCredential()) - .audience(BlobAudience.createBlobServiceAccountAudience("badAudience"))) + /* This test tests if the bearer challenge is working properly. A bad audience is passed in, the service returns + the default audience, and the request gets retried with this default audience, making the call function as expected. + */ + public void audienceErrorBearerChallengeRetry() { + BlockBlobAsyncClient aadBlob = getSpecializedBuilderWithTokenCredential(blockBlobAsyncClient.getBlobUrl()) + .audience(BlobAudience.createBlobServiceAccountAudience("badAudience")) .buildBlockBlobAsyncClient(); - StepVerifier.create(aadBlob.exists()) - .verifyErrorSatisfies(r -> { - BlobStorageException e = assertInstanceOf(BlobStorageException.class, r); - assertTrue(e.getErrorCode() == BlobErrorCode.INVALID_AUTHENTICATION_INFO); - }); + StepVerifier.create(aadBlob.getProperties()) + .assertNext(r -> assertNotNull(r)) + .verifyComplete(); } @Test diff --git a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/specialized/PageBlobApiTests.java b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/specialized/PageBlobApiTests.java index 15fc318bfb9b4..22f4a0e2f9b12 100644 --- a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/specialized/PageBlobApiTests.java +++ b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/specialized/PageBlobApiTests.java @@ -7,7 +7,6 @@ import com.azure.core.http.HttpRange; import com.azure.core.http.rest.PagedResponse; import com.azure.core.http.rest.Response; -import com.azure.core.test.utils.MockTokenCredential; import com.azure.core.test.utils.TestUtils; import com.azure.core.util.CoreUtils; import com.azure.storage.blob.BlobClient; @@ -39,6 +38,7 @@ import com.azure.storage.blob.sas.BlobContainerSasPermission; import com.azure.storage.blob.sas.BlobServiceSasSignatureValues; import com.azure.storage.common.implementation.Constants; +import com.azure.storage.common.test.shared.extensions.LiveOnly; import com.azure.storage.common.test.shared.extensions.RequiredServiceVersion; import com.azure.storage.common.test.shared.policy.TransientFailureInjectingHttpPipelinePolicy; import org.junit.jupiter.api.BeforeEach; @@ -537,6 +537,21 @@ public void uploadPageFromURLMin() { assertTrue(validateBasicHeaders(response.getHeaders())); } + /*@RequiredServiceVersion(clazz = BlobServiceVersion.class, min = "2024-08-04") + @Test + public void uploadPageFromURLSourceErrorAndStatusCode() { + PageBlobClient destBlob = cc.getBlobClient(generateBlobName()).getPageBlobClient(); + + destBlob.createIfNotExists(Constants.KB); + PageRange pageRange = new PageRange().setStart(0).setEnd(PageBlobClient.PAGE_BYTES - 1); + + BlobStorageException e = assertThrows(BlobStorageException.class, () -> destBlob.uploadPagesFromUrl(pageRange, bc.getBlobUrl(), null)); + + assertTrue(e.getStatusCode() == 409); + assertTrue(e.getServiceMessage().contains("PublicAccessNotPermitted")); + assertTrue(e.getServiceMessage().contains("Public access is not permitted on this storage account.")); + }*/ + @Test public void uploadPageFromURLRange() { byte[] data = getRandomByteArray(PageBlobClient.PAGE_BYTES * 4); @@ -1689,16 +1704,18 @@ public void storageAccountAudience() { assertTrue(aadBlob.exists()); } + @RequiredServiceVersion(clazz = BlobServiceVersion.class, min = "2024-08-04") + @LiveOnly @Test - public void audienceError() { - PageBlobClient aadBlob = instrument(new SpecializedBlobClientBuilder() - .endpoint(bc.getBlobUrl()) - .credential(new MockTokenCredential()) - .audience(BlobAudience.createBlobServiceAccountAudience("badAudience"))) + /* This test tests if the bearer challenge is working properly. A bad audience is passed in, the service returns + the default audience, and the request gets retried with this default audience, making the call function as expected. + */ + public void audienceErrorBearerChallengeRetry() { + PageBlobClient aadBlob = getSpecializedBuilderWithTokenCredential(bc.getBlobUrl()) + .audience(BlobAudience.createBlobServiceAccountAudience("badAudience")) .buildPageBlobClient(); - BlobStorageException e = assertThrows(BlobStorageException.class, () -> aadBlob.exists()); - assertTrue(e.getErrorCode() == BlobErrorCode.INVALID_AUTHENTICATION_INFO); + assertNotNull(aadBlob.getProperties()); } @Test diff --git a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/specialized/PageBlobAsyncApiTests.java b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/specialized/PageBlobAsyncApiTests.java index 9a95edb8e14a3..644ceb5e27351 100644 --- a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/specialized/PageBlobAsyncApiTests.java +++ b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/specialized/PageBlobAsyncApiTests.java @@ -6,7 +6,6 @@ import com.azure.core.exception.UnexpectedLengthException; import com.azure.core.http.HttpRange; import com.azure.core.http.rest.Response; -import com.azure.core.test.utils.MockTokenCredential; import com.azure.core.test.utils.TestUtils; import com.azure.core.util.CoreUtils; import com.azure.core.util.FluxUtil; @@ -34,6 +33,7 @@ import com.azure.storage.blob.sas.BlobSasPermission; import com.azure.storage.blob.sas.BlobServiceSasSignatureValues; import com.azure.storage.common.implementation.Constants; +import com.azure.storage.common.test.shared.extensions.LiveOnly; import com.azure.storage.common.test.shared.extensions.RequiredServiceVersion; import com.azure.storage.common.test.shared.policy.TransientFailureInjectingHttpPipelinePolicy; import org.junit.jupiter.api.BeforeEach; @@ -547,6 +547,22 @@ public void uploadPageFromURLMin() { .verifyComplete(); } + /*@RequiredServiceVersion(clazz = BlobServiceVersion.class, min = "2024-08-04") + @Test + public void uploadPageFromURLSourceErrorAndStatusCode() { + PageBlobAsyncClient destBlob = ccAsync.getBlobAsyncClient(generateBlobName()).getPageBlobAsyncClient(); + + PageRange pageRange = new PageRange().setStart(0).setEnd(PageBlobClient.PAGE_BYTES - 1); + + StepVerifier.create(destBlob.createIfNotExists(Constants.KB).then(destBlob.uploadPagesFromUrl(pageRange, bc.getBlobUrl(), null))) + .verifyErrorSatisfies(r -> { + BlobStorageException e = assertInstanceOf(BlobStorageException.class, r); + assertTrue(e.getStatusCode() == 409); + assertTrue(e.getServiceMessage().contains("PublicAccessNotPermitted")); + assertTrue(e.getServiceMessage().contains("Public access is not permitted on this storage account.")); + }); + }*/ + @Test public void uploadPageFromURLRange() { byte[] data = getRandomByteArray(PageBlobClient.PAGE_BYTES * 4); @@ -1661,19 +1677,20 @@ public void storageAccountAudience() { .verifyComplete(); } + @RequiredServiceVersion(clazz = BlobServiceVersion.class, min = "2024-08-04") + @LiveOnly @Test - public void audienceError() { - PageBlobAsyncClient aadBlob = instrument(new SpecializedBlobClientBuilder() - .endpoint(bc.getBlobUrl()) - .credential(new MockTokenCredential()) - .audience(BlobAudience.createBlobServiceAccountAudience("badAudience"))) + /* This test tests if the bearer challenge is working properly. A bad audience is passed in, the service returns + the default audience, and the request gets retried with this default audience, making the call function as expected. + */ + public void audienceErrorBearerChallengeRetry() { + PageBlobAsyncClient aadBlob = getSpecializedBuilderWithTokenCredential(bc.getBlobUrl()) + .audience(BlobAudience.createBlobServiceAccountAudience("badAudience")) .buildPageBlobAsyncClient(); - StepVerifier.create(aadBlob.exists()) - .verifyErrorSatisfies(r -> { - BlobStorageException e = assertInstanceOf(BlobStorageException.class, r); - assertTrue(e.getErrorCode() == BlobErrorCode.INVALID_AUTHENTICATION_INFO); - }); + StepVerifier.create(aadBlob.getProperties()) + .assertNext(r -> assertNotNull(r)) + .verifyComplete(); } @Test diff --git a/sdk/storage/azure-storage-blob/swagger/README.md b/sdk/storage/azure-storage-blob/swagger/README.md index 2e670ed47dd2a..73ff2a71122ba 100644 --- a/sdk/storage/azure-storage-blob/swagger/README.md +++ b/sdk/storage/azure-storage-blob/swagger/README.md @@ -16,7 +16,7 @@ autorest ### Code generation settings ``` yaml use: '@autorest/java@4.1.16' -input-file: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/a32d0b2423d19835246bb2ef92941503bfd5e734/specification/storage/data-plane/Microsoft.BlobStorage/preview/2021-12-02/blob.json +input-file: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/ccefad16baad361096d997888ef8ff8503118fac/specification/storage/data-plane/Microsoft.BlobStorage/stable/2024-08-04/blob.json java: true output-folder: ../ namespace: com.azure.storage.blob diff --git a/sdk/storage/azure-storage-common/ci.system.properties b/sdk/storage/azure-storage-common/ci.system.properties index c582bac08ccce..83ef1a7ed14de 100644 --- a/sdk/storage/azure-storage-common/ci.system.properties +++ b/sdk/storage/azure-storage-common/ci.system.properties @@ -1,2 +1,2 @@ -AZURE_LIVE_TEST_SERVICE_VERSION=V2024_05_04 -AZURE_STORAGE_SAS_SERVICE_VERSION=2024-05-04 +AZURE_LIVE_TEST_SERVICE_VERSION=V2024_08_04 +AZURE_STORAGE_SAS_SERVICE_VERSION=2024-08-04 diff --git a/sdk/storage/azure-storage-common/src/main/java/com/azure/storage/common/implementation/Constants.java b/sdk/storage/azure-storage-common/src/main/java/com/azure/storage/common/implementation/Constants.java index 24c7bd9f17bad..b1ac0667451a6 100644 --- a/sdk/storage/azure-storage-common/src/main/java/com/azure/storage/common/implementation/Constants.java +++ b/sdk/storage/azure-storage-common/src/main/java/com/azure/storage/common/implementation/Constants.java @@ -88,7 +88,7 @@ public final class Constants { public static final String PROPERTY_AZURE_STORAGE_SAS_SERVICE_VERSION = "AZURE_STORAGE_SAS_SERVICE_VERSION"; public static final String SAS_SERVICE_VERSION = Configuration.getGlobalConfiguration() - .get(PROPERTY_AZURE_STORAGE_SAS_SERVICE_VERSION, "2024-05-04"); + .get(PROPERTY_AZURE_STORAGE_SAS_SERVICE_VERSION, "2024-08-04"); private Constants() { } @@ -216,7 +216,7 @@ public static final class HeaderConstants { * @deprecated For SAS Service Version use {@link Constants#SAS_SERVICE_VERSION}. */ @Deprecated - public static final String TARGET_STORAGE_VERSION = "2024-05-04"; + public static final String TARGET_STORAGE_VERSION = "2024-08-04"; /** * Error code returned from the service. diff --git a/sdk/storage/azure-storage-common/src/main/java/com/azure/storage/common/policy/RequestRetryPolicy.java b/sdk/storage/azure-storage-common/src/main/java/com/azure/storage/common/policy/RequestRetryPolicy.java index 79230e1ebf07d..efa12a821d4d4 100644 --- a/sdk/storage/azure-storage-common/src/main/java/com/azure/storage/common/policy/RequestRetryPolicy.java +++ b/sdk/storage/azure-storage-common/src/main/java/com/azure/storage/common/policy/RequestRetryPolicy.java @@ -4,6 +4,7 @@ package com.azure.storage.common.policy; +import com.azure.core.http.HttpHeaderName; import com.azure.core.http.HttpMethod; import com.azure.core.http.HttpPipelineCallContext; import com.azure.core.http.HttpPipelineNextPolicy; @@ -39,6 +40,7 @@ public final class RequestRetryPolicy implements HttpPipelinePolicy { private static final ClientLogger LOGGER = new ClientLogger(RequestRetryPolicy.class); private final RequestRetryOptions requestRetryOptions; + private static final HttpHeaderName X_MS_COPY_SOURCE_ERROR_CODE = HttpHeaderName.fromString("x-ms-copy-source-error-code"); /** * Constructs the policy using the retry options. @@ -151,6 +153,7 @@ private Mono attemptAsync(HttpPipelineCallContext context, HttpPip boolean newConsiderSecondary = considerSecondary; int statusCode = response.getStatusCode(); + //boolean retry = shouldResponseBeRetried(statusCode, tryingPrimary, response); boolean retry = shouldStatusCodeBeRetried(statusCode, tryingPrimary); if (!tryingPrimary && statusCode == 404) { newConsiderSecondary = false; @@ -267,6 +270,7 @@ private HttpResponse attemptSync(final HttpPipelineCallContext context, HttpPipe boolean newConsiderSecondary = considerSecondary; int statusCode = response.getStatusCode(); boolean retry = shouldStatusCodeBeRetried(statusCode, tryingPrimary); + //boolean retry = shouldResponseBeRetried(statusCode, tryingPrimary, response); if (!tryingPrimary && statusCode == 404) { newConsiderSecondary = false; } @@ -377,16 +381,32 @@ static ExceptionRetryStatus shouldErrorBeRetried(Throwable error, int attempt, i return new ExceptionRetryStatus(false, unwrappedThrowable); } - static boolean shouldStatusCodeBeRetried(int statusCode, boolean isPrimary) { + + //static boolean shouldResponseBeRetried(int statusCode, boolean isPrimary, HttpResponse response) { /* * Retry the request if the server had an error (500), was unavailable (503), or requested a backoff (429), * or if the secondary was being tried and the resources didn't exist there (404). Only the secondary can retry * if the resource wasn't found as there may be a delay in replication from the primary. */ + //boolean headerRetry = false; + //boolean statusCodeRetry = (statusCode == 429 || statusCode == 500 || statusCode == 503) || (!isPrimary && statusCode == 404); + //if (response != null && response.getHeaders() != null) { + //String headerValue = response.getHeaders().getValue(X_MS_COPY_SOURCE_ERROR_CODE); + //if (headerValue != null) { + //headerRetry = ("429".equals(headerValue) || "500".equals(headerValue) || "503".equals(headerValue)) + //|| (!isPrimary && "404".equals(headerValue)); + //} + + //} + //return statusCodeRetry || headerRetry; + //} + + static boolean shouldStatusCodeBeRetried(int statusCode, boolean isPrimary) { return (statusCode == 429 || statusCode == 500 || statusCode == 503) || (!isPrimary && statusCode == 404); } + static final class ExceptionRetryStatus { final boolean canBeRetried; final Throwable unwrappedThrowable; diff --git a/sdk/storage/azure-storage-common/src/main/java/com/azure/storage/common/policy/StorageBearerTokenChallengeAuthorizationPolicy.java b/sdk/storage/azure-storage-common/src/main/java/com/azure/storage/common/policy/StorageBearerTokenChallengeAuthorizationPolicy.java new file mode 100644 index 0000000000000..1304665c36837 --- /dev/null +++ b/sdk/storage/azure-storage-common/src/main/java/com/azure/storage/common/policy/StorageBearerTokenChallengeAuthorizationPolicy.java @@ -0,0 +1,120 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.storage.common.policy; + +import com.azure.core.credential.TokenCredential; +import com.azure.core.credential.TokenRequestContext; +import com.azure.core.http.HttpHeaderName; +import com.azure.core.http.HttpPipelineCallContext; +import com.azure.core.http.HttpResponse; +import com.azure.core.http.policy.BearerTokenAuthenticationPolicy; +import com.azure.core.util.CoreUtils; +import reactor.core.publisher.Mono; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Locale; + +/** + * The storage authorization policy which supports challenge. + */ +public class StorageBearerTokenChallengeAuthorizationPolicy extends BearerTokenAuthenticationPolicy { + + private static final String DEFAULT_SCOPE = "/.default"; + private static final String BEARER_TOKEN_PREFIX = "Bearer "; + + private String[] scopes; + + /** + * Creates StorageBearerTokenChallengeAuthorizationPolicy. + * + * @param credential the token credential to authenticate the request + * @param scopes the scopes used in credential, using default scopes when empty + */ + public StorageBearerTokenChallengeAuthorizationPolicy(TokenCredential credential, String... scopes) { + super(credential, scopes); + this.scopes = scopes; + } + + @Override + public Mono authorizeRequest(HttpPipelineCallContext context) { + String[] scopes = this.scopes; + scopes = getScopes(context, scopes); + if (scopes == null) { + return Mono.empty(); + } else { + return setAuthorizationHeader(context, new TokenRequestContext().addScopes(scopes)); + } + } + + @Override + public void authorizeRequestSync(HttpPipelineCallContext context) { + String[] scopes = this.scopes; + scopes = getScopes(context, scopes); + + if (scopes != null) { + setAuthorizationHeaderSync(context, new TokenRequestContext().addScopes(scopes)); + } + } + + @Override + public Mono authorizeRequestOnChallenge(HttpPipelineCallContext context, HttpResponse response) { + String authHeader = response.getHeaderValue(HttpHeaderName.WWW_AUTHENTICATE); + Map challenges = extractChallengeAttributes(authHeader, BEARER_TOKEN_PREFIX); + + String scope = challenges.get("resource_id"); + if (scope != null) { + scope += DEFAULT_SCOPE; + scopes = new String[] { scope }; + scopes = getScopes(context, scopes); + return setAuthorizationHeader(context, new TokenRequestContext().addScopes(scopes)).thenReturn(true); + } + return Mono.just(false); + } + + @Override + public boolean authorizeRequestOnChallengeSync(HttpPipelineCallContext context, HttpResponse response) { + String authHeader = response.getHeaderValue(HttpHeaderName.WWW_AUTHENTICATE); + Map challenges = extractChallengeAttributes(authHeader, BEARER_TOKEN_PREFIX); + + String scope = challenges.get("resource_id"); + if (scope != null) { + scope += DEFAULT_SCOPE; + scopes = new String[] { scope }; + scopes = getScopes(context, scopes); + setAuthorizationHeaderSync(context, new TokenRequestContext().addScopes(scopes)); + return true; + } + return false; + } + + String[] getScopes(HttpPipelineCallContext context, String[] scopes) { + return CoreUtils.clone(scopes); + } + + Map extractChallengeAttributes(String header, String authChallengePrefix) { + if (!isBearerChallenge(header, authChallengePrefix)) { + return Collections.emptyMap(); + } + + header = header.toLowerCase(Locale.ROOT).replace(authChallengePrefix.toLowerCase(Locale.ROOT), ""); + + String[] attributes = header.split(" "); + Map attributeMap = new HashMap<>(); + + for (String pair : attributes) { + String[] keyValue = pair.split("="); + + attributeMap.put(keyValue[0].replaceAll("\"", ""), keyValue[1].replaceAll("\"", "")); + } + + return attributeMap; + } + + static boolean isBearerChallenge(String authenticateHeader, String authChallengePrefix) { + return (!CoreUtils.isNullOrEmpty(authenticateHeader) + && authenticateHeader.toLowerCase(Locale.ROOT).startsWith(authChallengePrefix.toLowerCase(Locale.ROOT))); + } +} diff --git a/sdk/storage/azure-storage-common/src/test/java/com/azure/storage/common/policy/RequestRetryPolicyTest.java b/sdk/storage/azure-storage-common/src/test/java/com/azure/storage/common/policy/RequestRetryPolicyTest.java index 1d2197e2677c2..21a99e2072992 100644 --- a/sdk/storage/azure-storage-common/src/test/java/com/azure/storage/common/policy/RequestRetryPolicyTest.java +++ b/sdk/storage/azure-storage-common/src/test/java/com/azure/storage/common/policy/RequestRetryPolicyTest.java @@ -247,6 +247,22 @@ public void retryPolicyRetriesExceptions(Throwable throwable, boolean shouldBeRe assertEquals(shouldBeRetried, RequestRetryPolicy.shouldErrorBeRetried(throwable, 0, 1).canBeRetried); } + /*@ParameterizedTest + @MethodSource("retryPolicyRetriesStatusCodeSupplier") + public void retryPolicyRetriesStatusCode(int statusCode, boolean isPrimary, boolean shouldBeRetried) { + assertEquals(shouldBeRetried, RequestRetryPolicy.shouldResponseBeRetried(statusCode, isPrimary, null)); + } + + @ParameterizedTest + @MethodSource("retryPolicyRetriesStatusCodeSupplier") + public void retryPolicyRetriesResponse(int statusCode, boolean isPrimary, boolean shouldBeRetried) { + MockHttpResponse response = new MockHttpResponse(null, 404, + new HttpHeaders().set(HttpHeaderName.fromString("x-ms-copy-source-error-code"), "" + statusCode)); + + assertEquals(shouldBeRetried, RequestRetryPolicy.shouldResponseBeRetried(0, isPrimary, response)); + + }*/ + @ParameterizedTest @MethodSource("retryPolicyRetriesStatusCodeSupplier") public void retryPolicyRetriesStatusCode(int statusCode, boolean isPrimary, boolean shouldBeRetried) { diff --git a/sdk/storage/azure-storage-file-datalake/CHANGELOG.md b/sdk/storage/azure-storage-file-datalake/CHANGELOG.md index 3405a9f085b74..3b9200fbcc06b 100644 --- a/sdk/storage/azure-storage-file-datalake/CHANGELOG.md +++ b/sdk/storage/azure-storage-file-datalake/CHANGELOG.md @@ -1,6 +1,7 @@ # Release History ## 12.20.0-beta.1 (Unreleased) +- Added support for bearer token challenges. ### Features Added diff --git a/sdk/storage/azure-storage-file-datalake/assets.json b/sdk/storage/azure-storage-file-datalake/assets.json index c7fa4607f6e01..32feb87474848 100644 --- a/sdk/storage/azure-storage-file-datalake/assets.json +++ b/sdk/storage/azure-storage-file-datalake/assets.json @@ -2,5 +2,5 @@ "AssetsRepo": "Azure/azure-sdk-assets", "AssetsRepoPrefixPath": "java", "TagPrefix": "java/storage/azure-storage-file-datalake", - "Tag": "java/storage/azure-storage-file-datalake_bc901016df" + "Tag": "java/storage/azure-storage-file-datalake_7c49e6e39d" } diff --git a/sdk/storage/azure-storage-file-datalake/src/main/java/com/azure/storage/file/datalake/DataLakeServiceVersion.java b/sdk/storage/azure-storage-file-datalake/src/main/java/com/azure/storage/file/datalake/DataLakeServiceVersion.java index 1965e090323d9..ff620070e5832 100644 --- a/sdk/storage/azure-storage-file-datalake/src/main/java/com/azure/storage/file/datalake/DataLakeServiceVersion.java +++ b/sdk/storage/azure-storage-file-datalake/src/main/java/com/azure/storage/file/datalake/DataLakeServiceVersion.java @@ -117,7 +117,12 @@ public enum DataLakeServiceVersion implements ServiceVersion { /** * Service version {@code 2024-05-04}. */ - V2024_05_04("2024-05-04"); + V2024_05_04("2024-05-04"), + + /** + * Service version {@code 2024-08-04}. + */ + V2024_08_04("2024-08-04"); private final String version; @@ -139,6 +144,6 @@ public String getVersion() { * @return the latest {@link DataLakeServiceVersion} */ public static DataLakeServiceVersion getLatest() { - return V2024_05_04; + return V2024_08_04; } } diff --git a/sdk/storage/azure-storage-file-datalake/src/main/java/com/azure/storage/file/datalake/implementation/util/BuilderHelper.java b/sdk/storage/azure-storage-file-datalake/src/main/java/com/azure/storage/file/datalake/implementation/util/BuilderHelper.java index 47b064437f067..f8cd73d74fa99 100644 --- a/sdk/storage/azure-storage-file-datalake/src/main/java/com/azure/storage/file/datalake/implementation/util/BuilderHelper.java +++ b/sdk/storage/azure-storage-file-datalake/src/main/java/com/azure/storage/file/datalake/implementation/util/BuilderHelper.java @@ -14,7 +14,6 @@ import com.azure.core.http.policy.AddHeadersFromContextPolicy; import com.azure.core.http.policy.AddHeadersPolicy; import com.azure.core.http.policy.AzureSasCredentialPolicy; -import com.azure.core.http.policy.BearerTokenAuthenticationPolicy; import com.azure.core.http.policy.HttpLogOptions; import com.azure.core.http.policy.HttpLoggingPolicy; import com.azure.core.http.policy.HttpPipelinePolicy; @@ -41,6 +40,7 @@ import com.azure.storage.common.policy.RequestRetryOptions; import com.azure.storage.common.policy.ResponseValidationPolicyBuilder; import com.azure.storage.common.policy.ScrubEtagPolicy; +import com.azure.storage.common.policy.StorageBearerTokenChallengeAuthorizationPolicy; import com.azure.storage.common.policy.StorageSharedKeyCredentialPolicy; import com.azure.storage.file.datalake.models.DataLakeAudience; @@ -128,7 +128,7 @@ public static HttpPipeline buildPipeline( String scope = audience != null ? ((audience.toString().endsWith("/") ? audience + ".default" : audience + "/.default")) : Constants.STORAGE_SCOPE; - credentialPolicy = new BearerTokenAuthenticationPolicy(tokenCredential, scope); + credentialPolicy = new StorageBearerTokenChallengeAuthorizationPolicy(tokenCredential, scope); } else if (azureSasCredential != null) { credentialPolicy = new AzureSasCredentialPolicy(azureSasCredential, false); } else { diff --git a/sdk/storage/azure-storage-file-datalake/src/main/java/com/azure/storage/file/datalake/implementation/util/TransformUtils.java b/sdk/storage/azure-storage-file-datalake/src/main/java/com/azure/storage/file/datalake/implementation/util/TransformUtils.java index fca9b07818bfd..54e3ea3bfa3ce 100644 --- a/sdk/storage/azure-storage-file-datalake/src/main/java/com/azure/storage/file/datalake/implementation/util/TransformUtils.java +++ b/sdk/storage/azure-storage-file-datalake/src/main/java/com/azure/storage/file/datalake/implementation/util/TransformUtils.java @@ -56,6 +56,8 @@ public static BlobServiceVersion toBlobServiceVersion(DataLakeServiceVersion ver return BlobServiceVersion.V2024_02_04; } else if (DataLakeServiceVersion.V2024_05_04.ordinal() == version.ordinal()) { return BlobServiceVersion.V2024_05_04; + } else if (DataLakeServiceVersion.V2024_08_04.ordinal() == version.ordinal()) { + return BlobServiceVersion.V2024_08_04; } return null; diff --git a/sdk/storage/azure-storage-file-datalake/src/test/java/com/azure/storage/file/datalake/DirectoryApiTests.java b/sdk/storage/azure-storage-file-datalake/src/test/java/com/azure/storage/file/datalake/DirectoryApiTests.java index 8c49534422a5b..77526ae7d9978 100644 --- a/sdk/storage/azure-storage-file-datalake/src/test/java/com/azure/storage/file/datalake/DirectoryApiTests.java +++ b/sdk/storage/azure-storage-file-datalake/src/test/java/com/azure/storage/file/datalake/DirectoryApiTests.java @@ -22,6 +22,7 @@ import com.azure.storage.common.sas.AccountSasResourceType; import com.azure.storage.common.sas.AccountSasService; import com.azure.storage.common.sas.AccountSasSignatureValues; +import com.azure.storage.common.test.shared.extensions.LiveOnly; import com.azure.storage.common.test.shared.extensions.RequiredServiceVersion; import com.azure.storage.file.datalake.implementation.util.DataLakeImplUtils; import com.azure.storage.file.datalake.models.AccessControlChangeCounters; @@ -3439,16 +3440,20 @@ public void storageAccountAudience() { assertTrue(aadDirClient.exists()); } + @RequiredServiceVersion(clazz = DataLakeServiceVersion.class, min = "2024-08-04") + @LiveOnly @Test - public void audienceError() { + /* This test tests if the bearer challenge is working properly. A bad audience is passed in, the service returns + the default audience, and the request gets retried with this default audience, making the call function as expected. + */ + public void audienceErrorBearerChallengeRetry() { DataLakeDirectoryClient aadDirClient = getPathClientBuilderWithTokenCredential( ENVIRONMENT.getDataLakeAccount().getDataLakeEndpoint(), dc.getDirectoryPath()) .fileSystemName(dataLakeFileSystemClient.getFileSystemName()) .audience(DataLakeAudience.createDataLakeServiceAccountAudience("badAudience")) .buildDirectoryClient(); - DataLakeStorageException e = assertThrows(DataLakeStorageException.class, aadDirClient::exists); - assertEquals(BlobErrorCode.INVALID_AUTHENTICATION_INFO.toString(), e.getErrorCode()); + assertTrue(aadDirClient.exists()); } @Test diff --git a/sdk/storage/azure-storage-file-datalake/src/test/java/com/azure/storage/file/datalake/DirectoryAsyncApiTests.java b/sdk/storage/azure-storage-file-datalake/src/test/java/com/azure/storage/file/datalake/DirectoryAsyncApiTests.java index 94a9755168c85..c6d964a90c627 100644 --- a/sdk/storage/azure-storage-file-datalake/src/test/java/com/azure/storage/file/datalake/DirectoryAsyncApiTests.java +++ b/sdk/storage/azure-storage-file-datalake/src/test/java/com/azure/storage/file/datalake/DirectoryAsyncApiTests.java @@ -20,6 +20,7 @@ import com.azure.storage.common.sas.AccountSasResourceType; import com.azure.storage.common.sas.AccountSasService; import com.azure.storage.common.sas.AccountSasSignatureValues; +import com.azure.storage.common.test.shared.extensions.LiveOnly; import com.azure.storage.common.test.shared.extensions.RequiredServiceVersion; import com.azure.storage.file.datalake.models.AccessControlChangeFailure; import com.azure.storage.file.datalake.models.AccessControlChangeResult; @@ -4141,8 +4142,13 @@ public void storageAccountAudience() { .verifyComplete(); } + @RequiredServiceVersion(clazz = DataLakeServiceVersion.class, min = "2024-08-04") + @LiveOnly @Test - public void audienceError() { + /* This test tests if the bearer challenge is working properly. A bad audience is passed in, the service returns + the default audience, and the request gets retried with this default audience, making the call function as expected. + */ + public void audienceErrorBearerChallengeRetry() { DataLakeDirectoryAsyncClient aadDirClient = getPathClientBuilderWithTokenCredential( ENVIRONMENT.getDataLakeAccount().getDataLakeEndpoint(), dc.getDirectoryPath()) .fileSystemName(dataLakeFileSystemAsyncClient.getFileSystemName()) @@ -4150,10 +4156,8 @@ public void audienceError() { .buildDirectoryAsyncClient(); StepVerifier.create(aadDirClient.exists()) - .verifyErrorSatisfies(r -> { - DataLakeStorageException e = assertInstanceOf(DataLakeStorageException.class, r); - assertEquals(BlobErrorCode.INVALID_AUTHENTICATION_INFO.toString(), e.getErrorCode()); - }); + .expectNext(true) + .verifyComplete(); } @Test diff --git a/sdk/storage/azure-storage-file-datalake/src/test/java/com/azure/storage/file/datalake/FileApiTest.java b/sdk/storage/azure-storage-file-datalake/src/test/java/com/azure/storage/file/datalake/FileApiTest.java index 0554b1ab592a1..696a3a6c9d50b 100644 --- a/sdk/storage/azure-storage-file-datalake/src/test/java/com/azure/storage/file/datalake/FileApiTest.java +++ b/sdk/storage/azure-storage-file-datalake/src/test/java/com/azure/storage/file/datalake/FileApiTest.java @@ -3412,16 +3412,20 @@ public void storageAccountAudience() { assertTrue(aadFileClient.exists()); } + @RequiredServiceVersion(clazz = DataLakeServiceVersion.class, min = "2024-08-04") + @LiveOnly @Test - public void audienceError() { + /* This test tests if the bearer challenge is working properly. A bad audience is passed in, the service returns + the default audience, and the request gets retried with this default audience, making the call function as expected. + */ + public void audienceErrorBearerChallengeRetry() { DataLakeFileClient aadFileClient = getPathClientBuilderWithTokenCredential( ENVIRONMENT.getDataLakeAccount().getDataLakeEndpoint(), fc.getFilePath()) .fileSystemName(dataLakeFileSystemClient.getFileSystemName()) .audience(DataLakeAudience.createDataLakeServiceAccountAudience("badAudience")) .buildFileClient(); - DataLakeStorageException e = assertThrows(DataLakeStorageException.class, aadFileClient::exists); - assertEquals(BlobErrorCode.INVALID_AUTHENTICATION_INFO.toString(), e.getErrorCode()); + assertTrue(aadFileClient.exists()); } @Test diff --git a/sdk/storage/azure-storage-file-datalake/src/test/java/com/azure/storage/file/datalake/FileAsyncApiTests.java b/sdk/storage/azure-storage-file-datalake/src/test/java/com/azure/storage/file/datalake/FileAsyncApiTests.java index d0a09c19b512b..3d1112747503e 100644 --- a/sdk/storage/azure-storage-file-datalake/src/test/java/com/azure/storage/file/datalake/FileAsyncApiTests.java +++ b/sdk/storage/azure-storage-file-datalake/src/test/java/com/azure/storage/file/datalake/FileAsyncApiTests.java @@ -4594,8 +4594,13 @@ public void storageAccountAudience() { .verifyComplete(); } + @RequiredServiceVersion(clazz = DataLakeServiceVersion.class, min = "2024-08-04") + @LiveOnly @Test - public void audienceError() { + /* This test tests if the bearer challenge is working properly. A bad audience is passed in, the service returns + the default audience, and the request gets retried with this default audience, making the call function as expected. + */ + public void audienceErrorBearerChallengeRetry() { DataLakeFileAsyncClient aadFileClient = getPathClientBuilderWithTokenCredential( ENVIRONMENT.getDataLakeAccount().getDataLakeEndpoint(), fc.getFilePath()) .fileSystemName(dataLakeFileSystemAsyncClient.getFileSystemName()) @@ -4603,10 +4608,8 @@ public void audienceError() { .buildFileAsyncClient(); StepVerifier.create(aadFileClient.exists()) - .verifyErrorSatisfies(r -> { - DataLakeStorageException e = assertInstanceOf(DataLakeStorageException.class, r); - assertEquals(BlobErrorCode.INVALID_AUTHENTICATION_INFO.toString(), e.getErrorCode()); - }); + .expectNext(true) + .verifyComplete(); } @Test diff --git a/sdk/storage/azure-storage-file-datalake/src/test/java/com/azure/storage/file/datalake/FileSystemApiTests.java b/sdk/storage/azure-storage-file-datalake/src/test/java/com/azure/storage/file/datalake/FileSystemApiTests.java index 34828afa616a4..528c87aa10fac 100644 --- a/sdk/storage/azure-storage-file-datalake/src/test/java/com/azure/storage/file/datalake/FileSystemApiTests.java +++ b/sdk/storage/azure-storage-file-datalake/src/test/java/com/azure/storage/file/datalake/FileSystemApiTests.java @@ -11,6 +11,7 @@ import com.azure.storage.blob.BlobUrlParts; import com.azure.storage.blob.models.BlobErrorCode; import com.azure.storage.common.test.shared.TestHttpClientType; +import com.azure.storage.common.test.shared.extensions.LiveOnly; import com.azure.storage.common.test.shared.extensions.RequiredServiceVersion; import com.azure.storage.file.datalake.models.DataLakeAccessPolicy; import com.azure.storage.file.datalake.models.DataLakeAudience; @@ -2294,15 +2295,20 @@ public void storageAccountAudience() { assertTrue(aadFsClient.exists()); } + @RequiredServiceVersion(clazz = DataLakeServiceVersion.class, min = "2024-08-04") + @LiveOnly @Test - public void audienceError() { + /* This test tests if the bearer challenge is working properly. A bad audience is passed in, the service returns + the default audience, and the request gets retried with this default audience, making the call function as expected. + */ + public void audienceErrorBearerChallengeRetry() { DataLakeFileSystemClient aadFsClient = getFileSystemClientBuilderWithTokenCredential(ENVIRONMENT.getDataLakeAccount().getDataLakeEndpoint()) + .fileSystemName(dataLakeFileSystemClient.getFileSystemName()) .audience(DataLakeAudience.createDataLakeServiceAccountAudience("badAudience")) .buildClient(); - DataLakeStorageException e = assertThrows(DataLakeStorageException.class, aadFsClient::exists); - assertEquals(BlobErrorCode.INVALID_AUTHENTICATION_INFO.toString(), e.getErrorCode()); + assertTrue(aadFsClient.exists()); } @Test diff --git a/sdk/storage/azure-storage-file-datalake/src/test/java/com/azure/storage/file/datalake/FileSystemAsyncApiTests.java b/sdk/storage/azure-storage-file-datalake/src/test/java/com/azure/storage/file/datalake/FileSystemAsyncApiTests.java index 43ca6eea44903..32fcf2b466ea5 100644 --- a/sdk/storage/azure-storage-file-datalake/src/test/java/com/azure/storage/file/datalake/FileSystemAsyncApiTests.java +++ b/sdk/storage/azure-storage-file-datalake/src/test/java/com/azure/storage/file/datalake/FileSystemAsyncApiTests.java @@ -10,6 +10,7 @@ import com.azure.storage.blob.BlobUrlParts; import com.azure.storage.blob.models.BlobErrorCode; import com.azure.storage.common.test.shared.TestHttpClientType; +import com.azure.storage.common.test.shared.extensions.LiveOnly; import com.azure.storage.common.test.shared.extensions.RequiredServiceVersion; import com.azure.storage.file.datalake.models.DataLakeAccessPolicy; import com.azure.storage.file.datalake.models.DataLakeAudience; @@ -2676,18 +2677,22 @@ public void storageAccountAudience() { .verifyComplete(); } + @RequiredServiceVersion(clazz = DataLakeServiceVersion.class, min = "2024-08-04") + @LiveOnly @Test - public void audienceError() { + /* This test tests if the bearer challenge is working properly. A bad audience is passed in, the service returns + the default audience, and the request gets retried with this default audience, making the call function as expected. + */ + public void audienceErrorBearerChallengeRetry() { DataLakeFileSystemAsyncClient aadFsClient = getFileSystemClientBuilderWithTokenCredential(ENVIRONMENT.getDataLakeAccount().getDataLakeEndpoint()) + .fileSystemName(dataLakeFileSystemAsyncClient.getFileSystemName()) .audience(DataLakeAudience.createDataLakeServiceAccountAudience("badAudience")) .buildAsyncClient(); StepVerifier.create(aadFsClient.exists()) - .verifyErrorSatisfies(r -> { - DataLakeStorageException e = assertInstanceOf(DataLakeStorageException.class, r); - assertEquals(BlobErrorCode.INVALID_AUTHENTICATION_INFO.toString(), e.getErrorCode()); - }); + .expectNext(true) + .verifyComplete(); } @Test diff --git a/sdk/storage/azure-storage-file-datalake/src/test/java/com/azure/storage/file/datalake/ServiceApiTests.java b/sdk/storage/azure-storage-file-datalake/src/test/java/com/azure/storage/file/datalake/ServiceApiTests.java index 2863bdec94df1..d0fd45d344c41 100644 --- a/sdk/storage/azure-storage-file-datalake/src/test/java/com/azure/storage/file/datalake/ServiceApiTests.java +++ b/sdk/storage/azure-storage-file-datalake/src/test/java/com/azure/storage/file/datalake/ServiceApiTests.java @@ -11,12 +11,12 @@ import com.azure.identity.DefaultAzureCredentialBuilder; import com.azure.storage.blob.BlobContainerClient; import com.azure.storage.blob.BlobUrlParts; -import com.azure.storage.blob.models.BlobErrorCode; import com.azure.storage.common.ParallelTransferOptions; import com.azure.storage.common.sas.AccountSasPermission; import com.azure.storage.common.sas.AccountSasResourceType; import com.azure.storage.common.sas.AccountSasService; import com.azure.storage.common.sas.AccountSasSignatureValues; +import com.azure.storage.common.test.shared.extensions.LiveOnly; import com.azure.storage.common.test.shared.extensions.RequiredServiceVersion; import com.azure.storage.file.datalake.models.DataLakeAnalyticsLogging; import com.azure.storage.file.datalake.models.DataLakeAudience; @@ -715,14 +715,18 @@ public void storageAccountAudience() { assertNotNull(aadServiceClient.getProperties()); } + @RequiredServiceVersion(clazz = DataLakeServiceVersion.class, min = "2024-08-04") + @LiveOnly @Test - public void audienceError() { + /* This test tests if the bearer challenge is working properly. A bad audience is passed in, the service returns + the default audience, and the request gets retried with this default audience, making the call function as expected. + */ + public void audienceErrorBearerChallengeRetry() { DataLakeServiceClient aadServiceClient = getOAuthServiceClientBuilder() .audience(DataLakeAudience.createDataLakeServiceAccountAudience("badAudience")) .buildClient(); - DataLakeStorageException e = assertThrows(DataLakeStorageException.class, aadServiceClient::getProperties); - assertEquals(BlobErrorCode.INVALID_AUTHENTICATION_INFO.toString(), e.getErrorCode()); + assertNotNull(aadServiceClient.getProperties()); } @Test diff --git a/sdk/storage/azure-storage-file-datalake/src/test/java/com/azure/storage/file/datalake/ServiceAsyncApiTests.java b/sdk/storage/azure-storage-file-datalake/src/test/java/com/azure/storage/file/datalake/ServiceAsyncApiTests.java index 382e8a2fdc6be..83d3dd00d4802 100644 --- a/sdk/storage/azure-storage-file-datalake/src/test/java/com/azure/storage/file/datalake/ServiceAsyncApiTests.java +++ b/sdk/storage/azure-storage-file-datalake/src/test/java/com/azure/storage/file/datalake/ServiceAsyncApiTests.java @@ -8,12 +8,12 @@ import com.azure.identity.DefaultAzureCredentialBuilder; import com.azure.storage.blob.BlobContainerClient; import com.azure.storage.blob.BlobUrlParts; -import com.azure.storage.blob.models.BlobErrorCode; import com.azure.storage.common.ParallelTransferOptions; import com.azure.storage.common.sas.AccountSasPermission; import com.azure.storage.common.sas.AccountSasResourceType; import com.azure.storage.common.sas.AccountSasService; import com.azure.storage.common.sas.AccountSasSignatureValues; +import com.azure.storage.common.test.shared.extensions.LiveOnly; import com.azure.storage.common.test.shared.extensions.RequiredServiceVersion; import com.azure.storage.file.datalake.models.DataLakeAnalyticsLogging; import com.azure.storage.file.datalake.models.DataLakeAudience; @@ -55,7 +55,6 @@ import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -709,17 +708,20 @@ public void storageAccountAudience() { .verifyComplete(); } + @RequiredServiceVersion(clazz = DataLakeServiceVersion.class, min = "2024-08-04") + @LiveOnly @Test - public void audienceError() { + /* This test tests if the bearer challenge is working properly. A bad audience is passed in, the service returns + the default audience, and the request gets retried with this default audience, making the call function as expected. + */ + public void audienceErrorBearerChallengeRetry() { DataLakeServiceAsyncClient aadServiceClient = getOAuthServiceClientBuilder() .audience(DataLakeAudience.createDataLakeServiceAccountAudience("badAudience")) .buildAsyncClient(); StepVerifier.create(aadServiceClient.getProperties()) - .verifyErrorSatisfies(r -> { - DataLakeStorageException e = assertInstanceOf(DataLakeStorageException.class, r); - assertEquals(BlobErrorCode.INVALID_AUTHENTICATION_INFO.toString(), e.getErrorCode()); - }); + .assertNext(Assertions::assertNotNull) + .verifyComplete(); } @Test diff --git a/sdk/storage/azure-storage-file-share/CHANGELOG.md b/sdk/storage/azure-storage-file-share/CHANGELOG.md index ddb2be3ad65b7..d440b41e5198c 100644 --- a/sdk/storage/azure-storage-file-share/CHANGELOG.md +++ b/sdk/storage/azure-storage-file-share/CHANGELOG.md @@ -1,6 +1,8 @@ # Release History ## 12.23.0-beta.1 (Unreleased) +- Added support for snapshot management on NFS shares. +- Added authorization error details in responses. ### Features Added diff --git a/sdk/storage/azure-storage-file-share/assets.json b/sdk/storage/azure-storage-file-share/assets.json index f13e44c89d35a..f98447b88900b 100644 --- a/sdk/storage/azure-storage-file-share/assets.json +++ b/sdk/storage/azure-storage-file-share/assets.json @@ -2,5 +2,5 @@ "AssetsRepo": "Azure/azure-sdk-assets", "AssetsRepoPrefixPath": "java", "TagPrefix": "java/storage/azure-storage-file-share", - "Tag": "java/storage/azure-storage-file-share_dfe3610108" + "Tag": "java/storage/azure-storage-file-share_79245de015" } diff --git a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/ShareAsyncClient.java b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/ShareAsyncClient.java index 1384b33f34e74..03bb9d993aabb 100644 --- a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/ShareAsyncClient.java +++ b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/ShareAsyncClient.java @@ -367,7 +367,8 @@ Mono> createWithResponse(ShareCreateOptions options, Context enabledProtocol = "".equals(enabledProtocol) ? null : enabledProtocol; return azureFileStorageClient.getShares() .createNoCustomHeadersWithResponseAsync(shareName, null, options.getMetadata(), options.getQuotaInGb(), - options.getAccessTier(), enabledProtocol, options.getRootSquash(), context) + options.getAccessTier(), enabledProtocol, options.getRootSquash(), + options.isSnapshotVirtualDirectoryAccessEnabled(), context) .map(ModelHelper::mapToShareInfoResponse); } @@ -923,7 +924,8 @@ Mono> setPropertiesWithResponse(ShareSetPropertiesOptions op ? new ShareRequestConditions() : options.getRequestConditions(); context = context == null ? Context.NONE : context; return azureFileStorageClient.getShares().setPropertiesNoCustomHeadersWithResponseAsync(shareName, null, - options.getQuotaInGb(), options.getAccessTier(), requestConditions.getLeaseId(), options.getRootSquash(), context) + options.getQuotaInGb(), options.getAccessTier(), requestConditions.getLeaseId(), options.getRootSquash(), + options.isSnapshotVirtualDirectoryAccessEnabled(), context) .map(ModelHelper::mapToShareInfoResponse); } diff --git a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/ShareClient.java b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/ShareClient.java index 3ce68779c263e..31febbc746434 100644 --- a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/ShareClient.java +++ b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/ShareClient.java @@ -355,9 +355,11 @@ public Response createWithResponse(ShareCreateOptions options, Durati ShareCreateOptions finalOptions = options == null ? new ShareCreateOptions() : options; String enabledProtocol = finalOptions.getProtocols() == null ? null : finalOptions.getProtocols().toString(); String finalEnabledProtocol = "".equals(enabledProtocol) ? null : enabledProtocol; + Callable> operation = () -> azureFileStorageClient.getShares() .createNoCustomHeadersWithResponse(shareName, null, finalOptions.getMetadata(), finalOptions.getQuotaInGb(), - finalOptions.getAccessTier(), finalEnabledProtocol, finalOptions.getRootSquash(), finalContext); + finalOptions.getAccessTier(), finalEnabledProtocol, finalOptions.getRootSquash(), + finalOptions.isSnapshotVirtualDirectoryAccessEnabled(), finalContext); return ModelHelper.mapToShareInfoResponse(sendRequest(operation, timeout, ShareStorageException.class)); } @@ -874,7 +876,8 @@ public Response setPropertiesWithResponse(ShareSetPropertiesOptions o Callable> operation = () -> this.azureFileStorageClient.getShares() .setPropertiesNoCustomHeadersWithResponse(shareName, null, options.getQuotaInGb(), options.getAccessTier(), - requestConditions.getLeaseId(), options.getRootSquash(), finalContext); + requestConditions.getLeaseId(), options.getRootSquash(), + options.isSnapshotVirtualDirectoryAccessEnabled(), finalContext); return ModelHelper.mapToShareInfoResponse(sendRequest(operation, timeout, ShareStorageException.class)); } diff --git a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/ShareServiceVersion.java b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/ShareServiceVersion.java index 23f6261bc44fb..2b8115e2e4f44 100644 --- a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/ShareServiceVersion.java +++ b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/ShareServiceVersion.java @@ -118,7 +118,12 @@ public enum ShareServiceVersion implements ServiceVersion { /** * Service version {@code 2024-05-04}. */ - V2024_05_04("2024-05-04"); + V2024_05_04("2024-05-04"), + + /** + * Service version {@code 2024-08-04}. + */ + V2024_08_04("2024-08-04"); private final String version; @@ -140,6 +145,6 @@ public String getVersion() { * @return the latest {@link ShareServiceVersion} */ public static ShareServiceVersion getLatest() { - return V2024_05_04; + return V2024_08_04; } } diff --git a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/SharesImpl.java b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/SharesImpl.java index e6af4f01d6965..1654b3ac9a556 100644 --- a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/SharesImpl.java +++ b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/SharesImpl.java @@ -92,8 +92,9 @@ Mono> create(@HostParam("url") String ur @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-meta-") Map metadata, @HeaderParam("x-ms-share-quota") Integer quota, @HeaderParam("x-ms-access-tier") ShareAccessTier accessTier, @HeaderParam("x-ms-version") String version, @HeaderParam("x-ms-enabled-protocols") String enabledProtocols, - @HeaderParam("x-ms-root-squash") ShareRootSquash rootSquash, @HeaderParam("Accept") String accept, - Context context); + @HeaderParam("x-ms-root-squash") ShareRootSquash rootSquash, + @HeaderParam("x-ms-enable-snapshot-virtual-directory-access") Boolean enableSnapshotVirtualDirectoryAccess, + @HeaderParam("Accept") String accept, Context context); @Put("/{shareName}") @ExpectedResponses({ 201 }) @@ -103,8 +104,9 @@ Mono> createNoCustomHeaders(@HostParam("url") String url, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-meta-") Map metadata, @HeaderParam("x-ms-share-quota") Integer quota, @HeaderParam("x-ms-access-tier") ShareAccessTier accessTier, @HeaderParam("x-ms-version") String version, @HeaderParam("x-ms-enabled-protocols") String enabledProtocols, - @HeaderParam("x-ms-root-squash") ShareRootSquash rootSquash, @HeaderParam("Accept") String accept, - Context context); + @HeaderParam("x-ms-root-squash") ShareRootSquash rootSquash, + @HeaderParam("x-ms-enable-snapshot-virtual-directory-access") Boolean enableSnapshotVirtualDirectoryAccess, + @HeaderParam("Accept") String accept, Context context); @Put("/{shareName}") @ExpectedResponses({ 201 }) @@ -114,8 +116,9 @@ ResponseBase createSync(@HostParam("url") String url, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-meta-") Map metadata, @HeaderParam("x-ms-share-quota") Integer quota, @HeaderParam("x-ms-access-tier") ShareAccessTier accessTier, @HeaderParam("x-ms-version") String version, @HeaderParam("x-ms-enabled-protocols") String enabledProtocols, - @HeaderParam("x-ms-root-squash") ShareRootSquash rootSquash, @HeaderParam("Accept") String accept, - Context context); + @HeaderParam("x-ms-root-squash") ShareRootSquash rootSquash, + @HeaderParam("x-ms-enable-snapshot-virtual-directory-access") Boolean enableSnapshotVirtualDirectoryAccess, + @HeaderParam("Accept") String accept, Context context); @Put("/{shareName}") @ExpectedResponses({ 201 }) @@ -125,8 +128,9 @@ Response createNoCustomHeadersSync(@HostParam("url") String url, @PathPara @HeaderParam("x-ms-meta-") Map metadata, @HeaderParam("x-ms-share-quota") Integer quota, @HeaderParam("x-ms-access-tier") ShareAccessTier accessTier, @HeaderParam("x-ms-version") String version, @HeaderParam("x-ms-enabled-protocols") String enabledProtocols, - @HeaderParam("x-ms-root-squash") ShareRootSquash rootSquash, @HeaderParam("Accept") String accept, - Context context); + @HeaderParam("x-ms-root-squash") ShareRootSquash rootSquash, + @HeaderParam("x-ms-enable-snapshot-virtual-directory-access") Boolean enableSnapshotVirtualDirectoryAccess, + @HeaderParam("Accept") String accept, Context context); @Get("/{shareName}") @ExpectedResponses({ 200 }) @@ -551,8 +555,9 @@ Mono> setProperties(@HostParam("u @QueryParam("comp") String comp, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @HeaderParam("x-ms-share-quota") Integer quota, @HeaderParam("x-ms-access-tier") ShareAccessTier accessTier, @HeaderParam("x-ms-lease-id") String leaseId, - @HeaderParam("x-ms-root-squash") ShareRootSquash rootSquash, @HeaderParam("Accept") String accept, - Context context); + @HeaderParam("x-ms-root-squash") ShareRootSquash rootSquash, + @HeaderParam("x-ms-enable-snapshot-virtual-directory-access") Boolean enableSnapshotVirtualDirectoryAccess, + @HeaderParam("Accept") String accept, Context context); @Put("/{shareName}") @ExpectedResponses({ 200 }) @@ -562,8 +567,9 @@ Mono> setPropertiesNoCustomHeaders(@HostParam("url") String url, @QueryParam("comp") String comp, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @HeaderParam("x-ms-share-quota") Integer quota, @HeaderParam("x-ms-access-tier") ShareAccessTier accessTier, @HeaderParam("x-ms-lease-id") String leaseId, - @HeaderParam("x-ms-root-squash") ShareRootSquash rootSquash, @HeaderParam("Accept") String accept, - Context context); + @HeaderParam("x-ms-root-squash") ShareRootSquash rootSquash, + @HeaderParam("x-ms-enable-snapshot-virtual-directory-access") Boolean enableSnapshotVirtualDirectoryAccess, + @HeaderParam("Accept") String accept, Context context); @Put("/{shareName}") @ExpectedResponses({ 200 }) @@ -573,8 +579,9 @@ ResponseBase setPropertiesSync(@HostParam("url @QueryParam("comp") String comp, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @HeaderParam("x-ms-share-quota") Integer quota, @HeaderParam("x-ms-access-tier") ShareAccessTier accessTier, @HeaderParam("x-ms-lease-id") String leaseId, - @HeaderParam("x-ms-root-squash") ShareRootSquash rootSquash, @HeaderParam("Accept") String accept, - Context context); + @HeaderParam("x-ms-root-squash") ShareRootSquash rootSquash, + @HeaderParam("x-ms-enable-snapshot-virtual-directory-access") Boolean enableSnapshotVirtualDirectoryAccess, + @HeaderParam("Accept") String accept, Context context); @Put("/{shareName}") @ExpectedResponses({ 200 }) @@ -584,8 +591,9 @@ Response setPropertiesNoCustomHeadersSync(@HostParam("url") String url, @QueryParam("comp") String comp, @QueryParam("timeout") Integer timeout, @HeaderParam("x-ms-version") String version, @HeaderParam("x-ms-share-quota") Integer quota, @HeaderParam("x-ms-access-tier") ShareAccessTier accessTier, @HeaderParam("x-ms-lease-id") String leaseId, - @HeaderParam("x-ms-root-squash") ShareRootSquash rootSquash, @HeaderParam("Accept") String accept, - Context context); + @HeaderParam("x-ms-root-squash") ShareRootSquash rootSquash, + @HeaderParam("x-ms-enable-snapshot-virtual-directory-access") Boolean enableSnapshotVirtualDirectoryAccess, + @HeaderParam("Accept") String accept, Context context); @Put("/{shareName}") @ExpectedResponses({ 200 }) @@ -793,6 +801,7 @@ Response restoreNoCustomHeadersSync(@HostParam("url") String url, * @param accessTier Specifies the access tier of the share. * @param enabledProtocols Protocols to enable on the share. * @param rootSquash Root squash to set on the share. Only valid for NFS shares. + * @param enableSnapshotVirtualDirectoryAccess The enableSnapshotVirtualDirectoryAccess parameter. * @throws IllegalArgumentException thrown if parameters fail the validation. * @throws ShareStorageException thrown if the request is rejected by server. * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. @@ -801,11 +810,12 @@ Response restoreNoCustomHeadersSync(@HostParam("url") String url, @ServiceMethod(returns = ReturnType.SINGLE) public Mono> createWithResponseAsync(String shareName, Integer timeout, Map metadata, Integer quota, ShareAccessTier accessTier, String enabledProtocols, - ShareRootSquash rootSquash) { + ShareRootSquash rootSquash, Boolean enableSnapshotVirtualDirectoryAccess) { final String restype = "share"; final String accept = "application/xml"; return FluxUtil.withContext(context -> service.create(this.client.getUrl(), shareName, restype, timeout, - metadata, quota, accessTier, this.client.getVersion(), enabledProtocols, rootSquash, accept, context)); + metadata, quota, accessTier, this.client.getVersion(), enabledProtocols, rootSquash, + enableSnapshotVirtualDirectoryAccess, accept, context)); } /** @@ -821,6 +831,7 @@ public Mono> createWithResponseAsync(Str * @param accessTier Specifies the access tier of the share. * @param enabledProtocols Protocols to enable on the share. * @param rootSquash Root squash to set on the share. Only valid for NFS shares. + * @param enableSnapshotVirtualDirectoryAccess The enableSnapshotVirtualDirectoryAccess parameter. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @throws ShareStorageException thrown if the request is rejected by server. @@ -830,11 +841,12 @@ public Mono> createWithResponseAsync(Str @ServiceMethod(returns = ReturnType.SINGLE) public Mono> createWithResponseAsync(String shareName, Integer timeout, Map metadata, Integer quota, ShareAccessTier accessTier, String enabledProtocols, - ShareRootSquash rootSquash, Context context) { + ShareRootSquash rootSquash, Boolean enableSnapshotVirtualDirectoryAccess, Context context) { final String restype = "share"; final String accept = "application/xml"; return service.create(this.client.getUrl(), shareName, restype, timeout, metadata, quota, accessTier, - this.client.getVersion(), enabledProtocols, rootSquash, accept, context); + this.client.getVersion(), enabledProtocols, rootSquash, enableSnapshotVirtualDirectoryAccess, accept, + context); } /** @@ -850,6 +862,7 @@ public Mono> createWithResponseAsync(Str * @param accessTier Specifies the access tier of the share. * @param enabledProtocols Protocols to enable on the share. * @param rootSquash Root squash to set on the share. Only valid for NFS shares. + * @param enableSnapshotVirtualDirectoryAccess The enableSnapshotVirtualDirectoryAccess parameter. * @throws IllegalArgumentException thrown if parameters fail the validation. * @throws ShareStorageException thrown if the request is rejected by server. * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. @@ -857,9 +870,10 @@ public Mono> createWithResponseAsync(Str */ @ServiceMethod(returns = ReturnType.SINGLE) public Mono createAsync(String shareName, Integer timeout, Map metadata, Integer quota, - ShareAccessTier accessTier, String enabledProtocols, ShareRootSquash rootSquash) { - return createWithResponseAsync(shareName, timeout, metadata, quota, accessTier, enabledProtocols, rootSquash) - .flatMap(ignored -> Mono.empty()); + ShareAccessTier accessTier, String enabledProtocols, ShareRootSquash rootSquash, + Boolean enableSnapshotVirtualDirectoryAccess) { + return createWithResponseAsync(shareName, timeout, metadata, quota, accessTier, enabledProtocols, rootSquash, + enableSnapshotVirtualDirectoryAccess).flatMap(ignored -> Mono.empty()); } /** @@ -875,6 +889,7 @@ public Mono createAsync(String shareName, Integer timeout, Map createAsync(String shareName, Integer timeout, Map createAsync(String shareName, Integer timeout, Map metadata, Integer quota, - ShareAccessTier accessTier, String enabledProtocols, ShareRootSquash rootSquash, Context context) { + ShareAccessTier accessTier, String enabledProtocols, ShareRootSquash rootSquash, + Boolean enableSnapshotVirtualDirectoryAccess, Context context) { return createWithResponseAsync(shareName, timeout, metadata, quota, accessTier, enabledProtocols, rootSquash, - context).flatMap(ignored -> Mono.empty()); + enableSnapshotVirtualDirectoryAccess, context).flatMap(ignored -> Mono.empty()); } /** @@ -901,6 +917,7 @@ public Mono createAsync(String shareName, Integer timeout, Map createAsync(String shareName, Integer timeout, Map> createNoCustomHeadersWithResponseAsync(String shareName, Integer timeout, Map metadata, Integer quota, ShareAccessTier accessTier, String enabledProtocols, - ShareRootSquash rootSquash) { + ShareRootSquash rootSquash, Boolean enableSnapshotVirtualDirectoryAccess) { final String restype = "share"; final String accept = "application/xml"; - return FluxUtil - .withContext(context -> service.createNoCustomHeaders(this.client.getUrl(), shareName, restype, timeout, - metadata, quota, accessTier, this.client.getVersion(), enabledProtocols, rootSquash, accept, context)); + return FluxUtil.withContext(context -> service.createNoCustomHeaders(this.client.getUrl(), shareName, restype, + timeout, metadata, quota, accessTier, this.client.getVersion(), enabledProtocols, rootSquash, + enableSnapshotVirtualDirectoryAccess, accept, context)); } /** @@ -930,6 +947,7 @@ public Mono> createNoCustomHeadersWithResponseAsync(String shareN * @param accessTier Specifies the access tier of the share. * @param enabledProtocols Protocols to enable on the share. * @param rootSquash Root squash to set on the share. Only valid for NFS shares. + * @param enableSnapshotVirtualDirectoryAccess The enableSnapshotVirtualDirectoryAccess parameter. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @throws ShareStorageException thrown if the request is rejected by server. @@ -939,11 +957,12 @@ public Mono> createNoCustomHeadersWithResponseAsync(String shareN @ServiceMethod(returns = ReturnType.SINGLE) public Mono> createNoCustomHeadersWithResponseAsync(String shareName, Integer timeout, Map metadata, Integer quota, ShareAccessTier accessTier, String enabledProtocols, - ShareRootSquash rootSquash, Context context) { + ShareRootSquash rootSquash, Boolean enableSnapshotVirtualDirectoryAccess, Context context) { final String restype = "share"; final String accept = "application/xml"; return service.createNoCustomHeaders(this.client.getUrl(), shareName, restype, timeout, metadata, quota, - accessTier, this.client.getVersion(), enabledProtocols, rootSquash, accept, context); + accessTier, this.client.getVersion(), enabledProtocols, rootSquash, enableSnapshotVirtualDirectoryAccess, + accept, context); } /** @@ -959,6 +978,7 @@ public Mono> createNoCustomHeadersWithResponseAsync(String shareN * @param accessTier Specifies the access tier of the share. * @param enabledProtocols Protocols to enable on the share. * @param rootSquash Root squash to set on the share. Only valid for NFS shares. + * @param enableSnapshotVirtualDirectoryAccess The enableSnapshotVirtualDirectoryAccess parameter. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @throws ShareStorageException thrown if the request is rejected by server. @@ -968,11 +988,12 @@ public Mono> createNoCustomHeadersWithResponseAsync(String shareN @ServiceMethod(returns = ReturnType.SINGLE) public ResponseBase createWithResponse(String shareName, Integer timeout, Map metadata, Integer quota, ShareAccessTier accessTier, String enabledProtocols, - ShareRootSquash rootSquash, Context context) { + ShareRootSquash rootSquash, Boolean enableSnapshotVirtualDirectoryAccess, Context context) { final String restype = "share"; final String accept = "application/xml"; return service.createSync(this.client.getUrl(), shareName, restype, timeout, metadata, quota, accessTier, - this.client.getVersion(), enabledProtocols, rootSquash, accept, context); + this.client.getVersion(), enabledProtocols, rootSquash, enableSnapshotVirtualDirectoryAccess, accept, + context); } /** @@ -988,14 +1009,17 @@ public ResponseBase createWithResponse(String shareNa * @param accessTier Specifies the access tier of the share. * @param enabledProtocols Protocols to enable on the share. * @param rootSquash Root squash to set on the share. Only valid for NFS shares. + * @param enableSnapshotVirtualDirectoryAccess The enableSnapshotVirtualDirectoryAccess parameter. * @throws IllegalArgumentException thrown if parameters fail the validation. * @throws ShareStorageException thrown if the request is rejected by server. * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. */ @ServiceMethod(returns = ReturnType.SINGLE) public void create(String shareName, Integer timeout, Map metadata, Integer quota, - ShareAccessTier accessTier, String enabledProtocols, ShareRootSquash rootSquash) { - createWithResponse(shareName, timeout, metadata, quota, accessTier, enabledProtocols, rootSquash, Context.NONE); + ShareAccessTier accessTier, String enabledProtocols, ShareRootSquash rootSquash, + Boolean enableSnapshotVirtualDirectoryAccess) { + createWithResponse(shareName, timeout, metadata, quota, accessTier, enabledProtocols, rootSquash, + enableSnapshotVirtualDirectoryAccess, Context.NONE); } /** @@ -1011,6 +1035,7 @@ public void create(String shareName, Integer timeout, Map metada * @param accessTier Specifies the access tier of the share. * @param enabledProtocols Protocols to enable on the share. * @param rootSquash Root squash to set on the share. Only valid for NFS shares. + * @param enableSnapshotVirtualDirectoryAccess The enableSnapshotVirtualDirectoryAccess parameter. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @throws ShareStorageException thrown if the request is rejected by server. @@ -1020,11 +1045,12 @@ public void create(String shareName, Integer timeout, Map metada @ServiceMethod(returns = ReturnType.SINGLE) public Response createNoCustomHeadersWithResponse(String shareName, Integer timeout, Map metadata, Integer quota, ShareAccessTier accessTier, String enabledProtocols, - ShareRootSquash rootSquash, Context context) { + ShareRootSquash rootSquash, Boolean enableSnapshotVirtualDirectoryAccess, Context context) { final String restype = "share"; final String accept = "application/xml"; return service.createNoCustomHeadersSync(this.client.getUrl(), shareName, restype, timeout, metadata, quota, - accessTier, this.client.getVersion(), enabledProtocols, rootSquash, accept, context); + accessTier, this.client.getVersion(), enabledProtocols, rootSquash, enableSnapshotVirtualDirectoryAccess, + accept, context); } /** @@ -3484,6 +3510,7 @@ public Response getPermissionNoCustomHeadersWithResponse(String * @param accessTier Specifies the access tier of the share. * @param leaseId If specified, the operation only succeeds if the resource's lease is active and matches this ID. * @param rootSquash Root squash to set on the share. Only valid for NFS shares. + * @param enableSnapshotVirtualDirectoryAccess The enableSnapshotVirtualDirectoryAccess parameter. * @throws IllegalArgumentException thrown if parameters fail the validation. * @throws ShareStorageException thrown if the request is rejected by server. * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. @@ -3491,12 +3518,14 @@ public Response getPermissionNoCustomHeadersWithResponse(String */ @ServiceMethod(returns = ReturnType.SINGLE) public Mono> setPropertiesWithResponseAsync(String shareName, - Integer timeout, Integer quota, ShareAccessTier accessTier, String leaseId, ShareRootSquash rootSquash) { + Integer timeout, Integer quota, ShareAccessTier accessTier, String leaseId, ShareRootSquash rootSquash, + Boolean enableSnapshotVirtualDirectoryAccess) { final String restype = "share"; final String comp = "properties"; final String accept = "application/xml"; return FluxUtil.withContext(context -> service.setProperties(this.client.getUrl(), shareName, restype, comp, - timeout, this.client.getVersion(), quota, accessTier, leaseId, rootSquash, accept, context)); + timeout, this.client.getVersion(), quota, accessTier, leaseId, rootSquash, + enableSnapshotVirtualDirectoryAccess, accept, context)); } /** @@ -3510,6 +3539,7 @@ public Mono> setPropertiesWithRes * @param accessTier Specifies the access tier of the share. * @param leaseId If specified, the operation only succeeds if the resource's lease is active and matches this ID. * @param rootSquash Root squash to set on the share. Only valid for NFS shares. + * @param enableSnapshotVirtualDirectoryAccess The enableSnapshotVirtualDirectoryAccess parameter. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @throws ShareStorageException thrown if the request is rejected by server. @@ -3519,12 +3549,12 @@ public Mono> setPropertiesWithRes @ServiceMethod(returns = ReturnType.SINGLE) public Mono> setPropertiesWithResponseAsync(String shareName, Integer timeout, Integer quota, ShareAccessTier accessTier, String leaseId, ShareRootSquash rootSquash, - Context context) { + Boolean enableSnapshotVirtualDirectoryAccess, Context context) { final String restype = "share"; final String comp = "properties"; final String accept = "application/xml"; return service.setProperties(this.client.getUrl(), shareName, restype, comp, timeout, this.client.getVersion(), - quota, accessTier, leaseId, rootSquash, accept, context); + quota, accessTier, leaseId, rootSquash, enableSnapshotVirtualDirectoryAccess, accept, context); } /** @@ -3538,6 +3568,7 @@ public Mono> setPropertiesWithRes * @param accessTier Specifies the access tier of the share. * @param leaseId If specified, the operation only succeeds if the resource's lease is active and matches this ID. * @param rootSquash Root squash to set on the share. Only valid for NFS shares. + * @param enableSnapshotVirtualDirectoryAccess The enableSnapshotVirtualDirectoryAccess parameter. * @throws IllegalArgumentException thrown if parameters fail the validation. * @throws ShareStorageException thrown if the request is rejected by server. * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. @@ -3545,9 +3576,9 @@ public Mono> setPropertiesWithRes */ @ServiceMethod(returns = ReturnType.SINGLE) public Mono setPropertiesAsync(String shareName, Integer timeout, Integer quota, ShareAccessTier accessTier, - String leaseId, ShareRootSquash rootSquash) { - return setPropertiesWithResponseAsync(shareName, timeout, quota, accessTier, leaseId, rootSquash) - .flatMap(ignored -> Mono.empty()); + String leaseId, ShareRootSquash rootSquash, Boolean enableSnapshotVirtualDirectoryAccess) { + return setPropertiesWithResponseAsync(shareName, timeout, quota, accessTier, leaseId, rootSquash, + enableSnapshotVirtualDirectoryAccess).flatMap(ignored -> Mono.empty()); } /** @@ -3561,6 +3592,7 @@ public Mono setPropertiesAsync(String shareName, Integer timeout, Integer * @param accessTier Specifies the access tier of the share. * @param leaseId If specified, the operation only succeeds if the resource's lease is active and matches this ID. * @param rootSquash Root squash to set on the share. Only valid for NFS shares. + * @param enableSnapshotVirtualDirectoryAccess The enableSnapshotVirtualDirectoryAccess parameter. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @throws ShareStorageException thrown if the request is rejected by server. @@ -3569,9 +3601,9 @@ public Mono setPropertiesAsync(String shareName, Integer timeout, Integer */ @ServiceMethod(returns = ReturnType.SINGLE) public Mono setPropertiesAsync(String shareName, Integer timeout, Integer quota, ShareAccessTier accessTier, - String leaseId, ShareRootSquash rootSquash, Context context) { - return setPropertiesWithResponseAsync(shareName, timeout, quota, accessTier, leaseId, rootSquash, context) - .flatMap(ignored -> Mono.empty()); + String leaseId, ShareRootSquash rootSquash, Boolean enableSnapshotVirtualDirectoryAccess, Context context) { + return setPropertiesWithResponseAsync(shareName, timeout, quota, accessTier, leaseId, rootSquash, + enableSnapshotVirtualDirectoryAccess, context).flatMap(ignored -> Mono.empty()); } /** @@ -3585,6 +3617,7 @@ public Mono setPropertiesAsync(String shareName, Integer timeout, Integer * @param accessTier Specifies the access tier of the share. * @param leaseId If specified, the operation only succeeds if the resource's lease is active and matches this ID. * @param rootSquash Root squash to set on the share. Only valid for NFS shares. + * @param enableSnapshotVirtualDirectoryAccess The enableSnapshotVirtualDirectoryAccess parameter. * @throws IllegalArgumentException thrown if parameters fail the validation. * @throws ShareStorageException thrown if the request is rejected by server. * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. @@ -3592,12 +3625,14 @@ public Mono setPropertiesAsync(String shareName, Integer timeout, Integer */ @ServiceMethod(returns = ReturnType.SINGLE) public Mono> setPropertiesNoCustomHeadersWithResponseAsync(String shareName, Integer timeout, - Integer quota, ShareAccessTier accessTier, String leaseId, ShareRootSquash rootSquash) { + Integer quota, ShareAccessTier accessTier, String leaseId, ShareRootSquash rootSquash, + Boolean enableSnapshotVirtualDirectoryAccess) { final String restype = "share"; final String comp = "properties"; final String accept = "application/xml"; return FluxUtil.withContext(context -> service.setPropertiesNoCustomHeaders(this.client.getUrl(), shareName, - restype, comp, timeout, this.client.getVersion(), quota, accessTier, leaseId, rootSquash, accept, context)); + restype, comp, timeout, this.client.getVersion(), quota, accessTier, leaseId, rootSquash, + enableSnapshotVirtualDirectoryAccess, accept, context)); } /** @@ -3611,6 +3646,7 @@ public Mono> setPropertiesNoCustomHeadersWithResponseAsync(String * @param accessTier Specifies the access tier of the share. * @param leaseId If specified, the operation only succeeds if the resource's lease is active and matches this ID. * @param rootSquash Root squash to set on the share. Only valid for NFS shares. + * @param enableSnapshotVirtualDirectoryAccess The enableSnapshotVirtualDirectoryAccess parameter. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @throws ShareStorageException thrown if the request is rejected by server. @@ -3619,12 +3655,14 @@ public Mono> setPropertiesNoCustomHeadersWithResponseAsync(String */ @ServiceMethod(returns = ReturnType.SINGLE) public Mono> setPropertiesNoCustomHeadersWithResponseAsync(String shareName, Integer timeout, - Integer quota, ShareAccessTier accessTier, String leaseId, ShareRootSquash rootSquash, Context context) { + Integer quota, ShareAccessTier accessTier, String leaseId, ShareRootSquash rootSquash, + Boolean enableSnapshotVirtualDirectoryAccess, Context context) { final String restype = "share"; final String comp = "properties"; final String accept = "application/xml"; return service.setPropertiesNoCustomHeaders(this.client.getUrl(), shareName, restype, comp, timeout, - this.client.getVersion(), quota, accessTier, leaseId, rootSquash, accept, context); + this.client.getVersion(), quota, accessTier, leaseId, rootSquash, enableSnapshotVirtualDirectoryAccess, + accept, context); } /** @@ -3638,6 +3676,7 @@ public Mono> setPropertiesNoCustomHeadersWithResponseAsync(String * @param accessTier Specifies the access tier of the share. * @param leaseId If specified, the operation only succeeds if the resource's lease is active and matches this ID. * @param rootSquash Root squash to set on the share. Only valid for NFS shares. + * @param enableSnapshotVirtualDirectoryAccess The enableSnapshotVirtualDirectoryAccess parameter. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @throws ShareStorageException thrown if the request is rejected by server. @@ -3646,12 +3685,14 @@ public Mono> setPropertiesNoCustomHeadersWithResponseAsync(String */ @ServiceMethod(returns = ReturnType.SINGLE) public ResponseBase setPropertiesWithResponse(String shareName, Integer timeout, - Integer quota, ShareAccessTier accessTier, String leaseId, ShareRootSquash rootSquash, Context context) { + Integer quota, ShareAccessTier accessTier, String leaseId, ShareRootSquash rootSquash, + Boolean enableSnapshotVirtualDirectoryAccess, Context context) { final String restype = "share"; final String comp = "properties"; final String accept = "application/xml"; return service.setPropertiesSync(this.client.getUrl(), shareName, restype, comp, timeout, - this.client.getVersion(), quota, accessTier, leaseId, rootSquash, accept, context); + this.client.getVersion(), quota, accessTier, leaseId, rootSquash, enableSnapshotVirtualDirectoryAccess, + accept, context); } /** @@ -3665,14 +3706,16 @@ public ResponseBase setPropertiesWithResponse( * @param accessTier Specifies the access tier of the share. * @param leaseId If specified, the operation only succeeds if the resource's lease is active and matches this ID. * @param rootSquash Root squash to set on the share. Only valid for NFS shares. + * @param enableSnapshotVirtualDirectoryAccess The enableSnapshotVirtualDirectoryAccess parameter. * @throws IllegalArgumentException thrown if parameters fail the validation. * @throws ShareStorageException thrown if the request is rejected by server. * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent. */ @ServiceMethod(returns = ReturnType.SINGLE) public void setProperties(String shareName, Integer timeout, Integer quota, ShareAccessTier accessTier, - String leaseId, ShareRootSquash rootSquash) { - setPropertiesWithResponse(shareName, timeout, quota, accessTier, leaseId, rootSquash, Context.NONE); + String leaseId, ShareRootSquash rootSquash, Boolean enableSnapshotVirtualDirectoryAccess) { + setPropertiesWithResponse(shareName, timeout, quota, accessTier, leaseId, rootSquash, + enableSnapshotVirtualDirectoryAccess, Context.NONE); } /** @@ -3686,6 +3729,7 @@ public void setProperties(String shareName, Integer timeout, Integer quota, Shar * @param accessTier Specifies the access tier of the share. * @param leaseId If specified, the operation only succeeds if the resource's lease is active and matches this ID. * @param rootSquash Root squash to set on the share. Only valid for NFS shares. + * @param enableSnapshotVirtualDirectoryAccess The enableSnapshotVirtualDirectoryAccess parameter. * @param context The context to associate with this operation. * @throws IllegalArgumentException thrown if parameters fail the validation. * @throws ShareStorageException thrown if the request is rejected by server. @@ -3694,12 +3738,14 @@ public void setProperties(String shareName, Integer timeout, Integer quota, Shar */ @ServiceMethod(returns = ReturnType.SINGLE) public Response setPropertiesNoCustomHeadersWithResponse(String shareName, Integer timeout, Integer quota, - ShareAccessTier accessTier, String leaseId, ShareRootSquash rootSquash, Context context) { + ShareAccessTier accessTier, String leaseId, ShareRootSquash rootSquash, + Boolean enableSnapshotVirtualDirectoryAccess, Context context) { final String restype = "share"; final String comp = "properties"; final String accept = "application/xml"; return service.setPropertiesNoCustomHeadersSync(this.client.getUrl(), shareName, restype, comp, timeout, - this.client.getVersion(), quota, accessTier, leaseId, rootSquash, accept, context); + this.client.getVersion(), quota, accessTier, leaseId, rootSquash, enableSnapshotVirtualDirectoryAccess, + accept, context); } /** diff --git a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/models/SharePropertiesInternal.java b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/models/SharePropertiesInternal.java index 81de746aaf062..de71e99475ed4 100644 --- a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/models/SharePropertiesInternal.java +++ b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/models/SharePropertiesInternal.java @@ -117,6 +117,11 @@ public final class SharePropertiesInternal implements XmlSerializable */ @@ -518,6 +523,27 @@ public SharePropertiesInternal setRootSquash(ShareRootSquash rootSquash) { return this; } + /** + * Get the enableSnapshotVirtualDirectoryAccess property: The EnableSnapshotVirtualDirectoryAccess property. + * + * @return the enableSnapshotVirtualDirectoryAccess value. + */ + public Boolean isEnableSnapshotVirtualDirectoryAccess() { + return this.enableSnapshotVirtualDirectoryAccess; + } + + /** + * Set the enableSnapshotVirtualDirectoryAccess property: The EnableSnapshotVirtualDirectoryAccess property. + * + * @param enableSnapshotVirtualDirectoryAccess the enableSnapshotVirtualDirectoryAccess value to set. + * @return the SharePropertiesInternal object itself. + */ + public SharePropertiesInternal + setEnableSnapshotVirtualDirectoryAccess(Boolean enableSnapshotVirtualDirectoryAccess) { + this.enableSnapshotVirtualDirectoryAccess = enableSnapshotVirtualDirectoryAccess; + return this; + } + /** * Get the metadata property: Dictionary of <string>. * @@ -567,6 +593,8 @@ public XmlWriter toXml(XmlWriter xmlWriter, String rootElementName) throws XMLSt this.leaseDuration == null ? null : this.leaseDuration.toString()); xmlWriter.writeStringElement("EnabledProtocols", this.enabledProtocols); xmlWriter.writeStringElement("RootSquash", this.rootSquash == null ? null : this.rootSquash.toString()); + xmlWriter.writeBooleanElement("EnableSnapshotVirtualDirectoryAccess", + this.enableSnapshotVirtualDirectoryAccess); if (this.metadata != null) { xmlWriter.writeStartElement("Metadata"); for (Map.Entry entry : this.metadata.entrySet()) { @@ -656,6 +684,9 @@ public static SharePropertiesInternal fromXml(XmlReader xmlReader, String rootEl } else if ("RootSquash".equals(elementName.getLocalPart())) { deserializedSharePropertiesInternal.rootSquash = ShareRootSquash.fromString(reader.getStringElement()); + } else if ("EnableSnapshotVirtualDirectoryAccess".equals(elementName.getLocalPart())) { + deserializedSharePropertiesInternal.enableSnapshotVirtualDirectoryAccess + = reader.getNullableElement(Boolean::parseBoolean); } else if ("Metadata".equals(elementName.getLocalPart())) { while (reader.nextElement() != XmlToken.END_ELEMENT) { if (deserializedSharePropertiesInternal.metadata == null) { diff --git a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/models/SharesGetPropertiesHeaders.java b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/models/SharesGetPropertiesHeaders.java index 9d1972b374b89..4ba3472358e3e 100644 --- a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/models/SharesGetPropertiesHeaders.java +++ b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/models/SharesGetPropertiesHeaders.java @@ -57,6 +57,11 @@ public final class SharesGetPropertiesHeaders { */ private DateTimeRfc1123 xMsAccessTierChangeTime; + /* + * The x-ms-enable-snapshot-virtual-directory-access property. + */ + private Boolean xMsEnableSnapshotVirtualDirectoryAccess; + /* * The x-ms-meta- property. */ @@ -136,6 +141,9 @@ public final class SharesGetPropertiesHeaders { private static final HttpHeaderName X_MS_ACCESS_TIER_CHANGE_TIME = HttpHeaderName.fromString("x-ms-access-tier-change-time"); + private static final HttpHeaderName X_MS_ENABLE_SNAPSHOT_VIRTUAL_DIRECTORY_ACCESS + = HttpHeaderName.fromString("x-ms-enable-snapshot-virtual-directory-access"); + private static final HttpHeaderName X_MS_SHARE_PROVISIONED_INGRESS_MBPS = HttpHeaderName.fromString("x-ms-share-provisioned-ingress-mbps"); @@ -191,6 +199,12 @@ public SharesGetPropertiesHeaders(HttpHeaders rawHeaders) { if (xMsAccessTierChangeTime != null) { this.xMsAccessTierChangeTime = new DateTimeRfc1123(xMsAccessTierChangeTime); } + String xMsEnableSnapshotVirtualDirectoryAccess + = rawHeaders.getValue(X_MS_ENABLE_SNAPSHOT_VIRTUAL_DIRECTORY_ACCESS); + if (xMsEnableSnapshotVirtualDirectoryAccess != null) { + this.xMsEnableSnapshotVirtualDirectoryAccess + = Boolean.parseBoolean(xMsEnableSnapshotVirtualDirectoryAccess); + } String date = rawHeaders.getValue(HttpHeaderName.DATE); if (date != null) { this.date = new DateTimeRfc1123(date); @@ -391,6 +405,29 @@ public SharesGetPropertiesHeaders setXMsAccessTierChangeTime(OffsetDateTime xMsA return this; } + /** + * Get the xMsEnableSnapshotVirtualDirectoryAccess property: The x-ms-enable-snapshot-virtual-directory-access + * property. + * + * @return the xMsEnableSnapshotVirtualDirectoryAccess value. + */ + public Boolean isXMsEnableSnapshotVirtualDirectoryAccess() { + return this.xMsEnableSnapshotVirtualDirectoryAccess; + } + + /** + * Set the xMsEnableSnapshotVirtualDirectoryAccess property: The x-ms-enable-snapshot-virtual-directory-access + * property. + * + * @param xMsEnableSnapshotVirtualDirectoryAccess the xMsEnableSnapshotVirtualDirectoryAccess value to set. + * @return the SharesGetPropertiesHeaders object itself. + */ + public SharesGetPropertiesHeaders + setXMsEnableSnapshotVirtualDirectoryAccess(Boolean xMsEnableSnapshotVirtualDirectoryAccess) { + this.xMsEnableSnapshotVirtualDirectoryAccess = xMsEnableSnapshotVirtualDirectoryAccess; + return this; + } + /** * Get the xMsMeta property: The x-ms-meta- property. * diff --git a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/models/StorageError.java b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/models/StorageError.java index 2089268396b07..6afb2e2f2c28e 100644 --- a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/models/StorageError.java +++ b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/models/StorageError.java @@ -23,6 +23,26 @@ public final class StorageError implements XmlSerializable { */ private String message; + /* + * The CopySourceStatusCode property. + */ + private Long copySourceStatusCode; + + /* + * The CopySourceErrorCode property. + */ + private String copySourceErrorCode; + + /* + * The CopySourceErrorMessage property. + */ + private String copySourceErrorMessage; + + /* + * The AuthenticationErrorDetail property. + */ + private String authenticationErrorDetail; + /** * Creates an instance of StorageError class. */ @@ -49,6 +69,86 @@ public StorageError setMessage(String message) { return this; } + /** + * Get the copySourceStatusCode property: The CopySourceStatusCode property. + * + * @return the copySourceStatusCode value. + */ + public Long getCopySourceStatusCode() { + return this.copySourceStatusCode; + } + + /** + * Set the copySourceStatusCode property: The CopySourceStatusCode property. + * + * @param copySourceStatusCode the copySourceStatusCode value to set. + * @return the StorageError object itself. + */ + public StorageError setCopySourceStatusCode(Long copySourceStatusCode) { + this.copySourceStatusCode = copySourceStatusCode; + return this; + } + + /** + * Get the copySourceErrorCode property: The CopySourceErrorCode property. + * + * @return the copySourceErrorCode value. + */ + public String getCopySourceErrorCode() { + return this.copySourceErrorCode; + } + + /** + * Set the copySourceErrorCode property: The CopySourceErrorCode property. + * + * @param copySourceErrorCode the copySourceErrorCode value to set. + * @return the StorageError object itself. + */ + public StorageError setCopySourceErrorCode(String copySourceErrorCode) { + this.copySourceErrorCode = copySourceErrorCode; + return this; + } + + /** + * Get the copySourceErrorMessage property: The CopySourceErrorMessage property. + * + * @return the copySourceErrorMessage value. + */ + public String getCopySourceErrorMessage() { + return this.copySourceErrorMessage; + } + + /** + * Set the copySourceErrorMessage property: The CopySourceErrorMessage property. + * + * @param copySourceErrorMessage the copySourceErrorMessage value to set. + * @return the StorageError object itself. + */ + public StorageError setCopySourceErrorMessage(String copySourceErrorMessage) { + this.copySourceErrorMessage = copySourceErrorMessage; + return this; + } + + /** + * Get the authenticationErrorDetail property: The AuthenticationErrorDetail property. + * + * @return the authenticationErrorDetail value. + */ + public String getAuthenticationErrorDetail() { + return this.authenticationErrorDetail; + } + + /** + * Set the authenticationErrorDetail property: The AuthenticationErrorDetail property. + * + * @param authenticationErrorDetail the authenticationErrorDetail value to set. + * @return the StorageError object itself. + */ + public StorageError setAuthenticationErrorDetail(String authenticationErrorDetail) { + this.authenticationErrorDetail = authenticationErrorDetail; + return this; + } + @Override public XmlWriter toXml(XmlWriter xmlWriter) throws XMLStreamException { return toXml(xmlWriter, null); @@ -59,6 +159,10 @@ public XmlWriter toXml(XmlWriter xmlWriter, String rootElementName) throws XMLSt rootElementName = CoreUtils.isNullOrEmpty(rootElementName) ? "StorageError" : rootElementName; xmlWriter.writeStartElement(rootElementName); xmlWriter.writeStringElement("Message", this.message); + xmlWriter.writeNumberElement("CopySourceStatusCode", this.copySourceStatusCode); + xmlWriter.writeStringElement("CopySourceErrorCode", this.copySourceErrorCode); + xmlWriter.writeStringElement("CopySourceErrorMessage", this.copySourceErrorMessage); + xmlWriter.writeStringElement("AuthenticationErrorDetail", this.authenticationErrorDetail); return xmlWriter.writeEndElement(); } @@ -93,6 +197,14 @@ public static StorageError fromXml(XmlReader xmlReader, String rootElementName) if ("Message".equals(elementName.getLocalPart())) { deserializedStorageError.message = reader.getStringElement(); + } else if ("CopySourceStatusCode".equals(elementName.getLocalPart())) { + deserializedStorageError.copySourceStatusCode = reader.getNullableElement(Long::parseLong); + } else if ("CopySourceErrorCode".equals(elementName.getLocalPart())) { + deserializedStorageError.copySourceErrorCode = reader.getStringElement(); + } else if ("CopySourceErrorMessage".equals(elementName.getLocalPart())) { + deserializedStorageError.copySourceErrorMessage = reader.getStringElement(); + } else if ("AuthenticationErrorDetail".equals(elementName.getLocalPart())) { + deserializedStorageError.authenticationErrorDetail = reader.getStringElement(); } else { reader.skipElement(); } diff --git a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/util/ModelHelper.java b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/util/ModelHelper.java index c25f4984d7f7a..a3922ab5bac9b 100644 --- a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/util/ModelHelper.java +++ b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/implementation/util/ModelHelper.java @@ -202,6 +202,7 @@ public static ShareProperties populateShareProperties(SharePropertiesInternal sh properties.setRootSquash(sharePropertiesInternal.getRootSquash()); properties.setMetadata(sharePropertiesInternal.getMetadata()); properties.setProvisionedBandwidthMiBps(sharePropertiesInternal.getProvisionedBandwidthMiBps()); + properties.setSnapshotVirtualDirectoryAccessEnabled(sharePropertiesInternal.isEnableSnapshotVirtualDirectoryAccess()); return properties; } @@ -459,6 +460,7 @@ public static Response mapGetPropertiesResponse(ResponseBase(response, shareProperties); diff --git a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/models/ShareProperties.java b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/models/ShareProperties.java index 2a078286200c5..161729a5a7663 100644 --- a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/models/ShareProperties.java +++ b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/models/ShareProperties.java @@ -121,6 +121,11 @@ public final class ShareProperties implements XmlSerializable { */ private Integer provisionedBandwidthMiBps; + /* + * The EnableSnapshotVirtualDirectoryAccess property. + */ + private Boolean enableSnapshotVirtualDirectoryAccess; + /** * Get the lastModified property: The lastModified property. * @@ -559,6 +564,33 @@ public ShareProperties setMetadata(Map metadata) { return this; } + /** + * Get the enableSnapshotVirtualDirectoryAccess property: + * Optional. Supported in version 2023-08-03 and above. Only applicable for premium file storage accounts. + * Specifies whether the snapshot virtual directory should be accessible at the root of share mount point when NFS is enabled. + * If not specified, the default is true. + * + * @return the enableSnapshotVirtualDirectoryAccess value. + */ + public Boolean isSnapshotVirtualDirectoryAccessEnabled() { + return this.enableSnapshotVirtualDirectoryAccess; + } + + /** + * Set the enableSnapshotVirtualDirectoryAccess property: + * Optional. Supported in version 2023-08-03 and above. Only applicable for premium file storage accounts. + * Specifies whether the snapshot virtual directory should be accessible at the root of share mount point when NFS is enabled. + * If not specified, the default is true. + * + * @param enableSnapshotVirtualDirectoryAccess the enableSnapshotVirtualDirectoryAccess value to set. + * @return the ShareProperties object itself. + */ + public ShareProperties setSnapshotVirtualDirectoryAccessEnabled( + Boolean enableSnapshotVirtualDirectoryAccess) { + this.enableSnapshotVirtualDirectoryAccess = enableSnapshotVirtualDirectoryAccess; + return this; + } + @Override public XmlWriter toXml(XmlWriter xmlWriter) throws XMLStreamException { return toXml(xmlWriter, null); diff --git a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/options/ShareCreateOptions.java b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/options/ShareCreateOptions.java index 7a4e353ebbabb..b0fff0a403ac2 100644 --- a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/options/ShareCreateOptions.java +++ b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/options/ShareCreateOptions.java @@ -20,6 +20,7 @@ public class ShareCreateOptions { private ShareAccessTier accessTier; private ShareProtocols protocols; private ShareRootSquash rootSquash; + private Boolean enableSnapshotVirtualDirectoryAccess; /** * Creates a new instance of {@link ShareCreateOptions}. @@ -127,4 +128,29 @@ public ShareCreateOptions setRootSquash(ShareRootSquash rootSquash) { this.rootSquash = rootSquash; return this; } + + /** + * Get the enableSnapshotVirtualDirectoryAccess property: The EnableSnapshotVirtualDirectoryAccess property. + * Optional. Supported in version 2023-08-03 and above. Only applicable for premium file storage accounts. + * Specifies whether the snapshot virtual directory should be accessible at the root of share mount point when NFS is enabled. + * If not specified, the default is true. + * @return the enableSnapshotVirtualDirectoryAccess value. + */ + public Boolean isSnapshotVirtualDirectoryAccessEnabled() { + return enableSnapshotVirtualDirectoryAccess; + } + + /** + * Set the enableSnapshotVirtualDirectoryAccess property: + * Optional. Supported in version 2023-08-03 and above. Only applicable for premium file storage accounts. + * Specifies whether the snapshot virtual directory should be accessible at the root of share mount point when NFS is enabled. + * If not specified, the default is true. + * @param snapshotVirtualDirectoryAccessEnabled the enableSnapshotVirtualDirectoryAccess value to set. + * @return the ShareCreateOptions object itself. + */ + public ShareCreateOptions setSnapshotVirtualDirectoryAccessEnabled( + Boolean snapshotVirtualDirectoryAccessEnabled) { + this.enableSnapshotVirtualDirectoryAccess = snapshotVirtualDirectoryAccessEnabled; + return this; + } } diff --git a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/options/ShareSetPropertiesOptions.java b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/options/ShareSetPropertiesOptions.java index 7086bd21aa319..9fae36f74782a 100644 --- a/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/options/ShareSetPropertiesOptions.java +++ b/sdk/storage/azure-storage-file-share/src/main/java/com/azure/storage/file/share/options/ShareSetPropertiesOptions.java @@ -18,6 +18,7 @@ public class ShareSetPropertiesOptions { private ShareAccessTier accessTier; private ShareRootSquash rootSquash; private ShareRequestConditions requestConditions; + private Boolean enableSnapshotVirtualDirectoryAccess; /** * @return {@link ShareAccessTier} @@ -82,4 +83,31 @@ public ShareSetPropertiesOptions setRequestConditions(ShareRequestConditions req this.requestConditions = requestConditions; return this; } + + /** + * Get the enableSnapshotVirtualDirectoryAccess property: The EnableSnapshotVirtualDirectoryAccess property. + * Optional. Supported in version 2023-08-03 and above. Only applicable for premium file storage accounts. + * Specifies whether the snapshot virtual directory should be accessible at the root of share mount point when NFS is enabled. + * If not specified, the default is true. + * + * @return the enableSnapshotVirtualDirectoryAccess value. + */ + public Boolean isSnapshotVirtualDirectoryAccessEnabled() { + return enableSnapshotVirtualDirectoryAccess; + } + + /** + * Set the enableSnapshotVirtualDirectoryAccess property: The EnableSnapshotVirtualDirectoryAccess property. + * Optional. Supported in version 2023-08-03 and above. Only applicable for premium file storage accounts. + * Specifies whether the snapshot virtual directory should be accessible at the root of share mount point when NFS is enabled. + * If not specified, the default is true. + * + * @param snapshotVirtualDirectoryAccessEnabled the enableSnapshotVirtualDirectoryAccess value to set. + * @return the ShareSetPropertiesOptions object itself. + */ + public ShareSetPropertiesOptions setSnapshotVirtualDirectoryAccessEnabled( + Boolean snapshotVirtualDirectoryAccessEnabled) { + this.enableSnapshotVirtualDirectoryAccess = snapshotVirtualDirectoryAccessEnabled; + return this; + } } diff --git a/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/FileApiTests.java b/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/FileApiTests.java index be57161f1f0b7..be65b8674fb54 100644 --- a/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/FileApiTests.java +++ b/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/FileApiTests.java @@ -1102,6 +1102,21 @@ public void uploadRangeFromURL(String pathSuffix) { } } + /*@RequiredServiceVersion(clazz = ShareServiceVersion.class, min = "2024-08-04") + @Test + public void uploadRangeFromURLSourceErrorAndStatusCode() { + primaryFileClient.create(1024); + ShareFileClient destinationClient = shareClient.getFileClient(generatePathName()); + destinationClient.create(1024); + + ShareStorageException e = assertThrows(ShareStorageException.class, + () -> destinationClient.uploadRangeFromUrl(5, 0, 0, primaryFileClient.getFileUrl())); + + assertTrue(e.getStatusCode() == 401); + assertTrue(e.getServiceMessage().contains("NoAuthenticationInformation")); + assertTrue(e.getServiceMessage().contains("Server failed to authenticate the request. Please refer to the information in the www-authenticate header.")); + }*/ + @RequiredServiceVersion(clazz = ShareServiceVersion.class, min = "2021-04-10") @Test public void uploadRangeFromURLOAuth() { @@ -1348,6 +1363,21 @@ public void startCopyError() { FileShareTestHelper.assertExceptionStatusCodeAndMessage(e, 400, ShareErrorCode.INVALID_HEADER_VALUE); } + /*@RequiredServiceVersion(clazz = ShareServiceVersion.class, min = "2024-08-04") + @Test + public void startCopySourceErrorAndStatusCode() { + primaryFileClient.create(1024); + + ShareStorageException e = assertThrows(ShareStorageException.class, () -> { + SyncPoller poller = primaryFileClient.beginCopy("https://error.file.core.windows.net/garbage", testMetadata, null); + poller.waitForCompletion(); + }); + + assertTrue(e.getStatusCode() == 400); + assertTrue(e.getServiceMessage().contains("InvalidUri")); + assertTrue(e.getServiceMessage().contains("The requested URI does not represent any resource on the server.")); + }*/ + @ParameterizedTest @MethodSource("com.azure.storage.file.share.FileShareTestHelper#startCopyArgumentsSupplier") public void startCopyWithOptions(boolean setFilePermissionKey, boolean setFilePermission, boolean ignoreReadOnly, diff --git a/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/FileAsyncApiTests.java b/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/FileAsyncApiTests.java index b374c62f688bc..be1b552a9887c 100644 --- a/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/FileAsyncApiTests.java +++ b/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/FileAsyncApiTests.java @@ -76,7 +76,7 @@ public class FileAsyncApiTests extends FileShareTestBase { private ShareFileAsyncClient primaryFileAsyncClient; - private ShareClient shareClient; + private ShareAsyncClient shareAsyncClient; private String shareName; private String filePath; private static Map testMetadata; @@ -88,8 +88,8 @@ public class FileAsyncApiTests extends FileShareTestBase { public void setup() { shareName = generateShareName(); filePath = generatePathName(); - shareClient = shareBuilderHelper(shareName).buildClient(); - shareClient.create(); + shareAsyncClient = shareBuilderHelper(shareName).buildAsyncClient(); + shareAsyncClient.create().block(); primaryFileAsyncClient = fileBuilderHelper(shareName, filePath).buildFileAsyncClient(); testMetadata = Collections.singletonMap("testmetadata", "value"); httpHeaders = new ShareFileHttpHeaders().setContentLanguage("en") @@ -120,7 +120,7 @@ public void createFileError() { @Test public void createFileWithArgsFpk() { - String filePermissionKey = shareClient.createPermission(FILE_PERMISSION); + String filePermissionKey = shareAsyncClient.createPermission(FILE_PERMISSION).block(); smbProperties.setFileCreationTime(testResourceNamer.now()) .setFileLastWriteTime(testResourceNamer.now()) .setFilePermissionKey(filePermissionKey); @@ -608,6 +608,21 @@ public void uploadRangeFromURL() { }).verifyComplete(); } + /*@RequiredServiceVersion(clazz = ShareServiceVersion.class, min = "2024-08-04") + @Test + public void uploadRangeFromURLSourceErrorAndStatusCode() { + ShareFileAsyncClient destinationClient = shareAsyncClient.getFileClient(generatePathName()); + + StepVerifier.create(primaryFileAsyncClient.create(1024).then(destinationClient.create(1024)) + .then(destinationClient.uploadRangeFromUrl(5, 0, 0, primaryFileAsyncClient.getFileUrl()))) + .verifyErrorSatisfies(r -> { + ShareStorageException e = assertInstanceOf(ShareStorageException.class, r); + assertTrue(e.getStatusCode() == 401); + assertTrue(e.getServiceMessage().contains("NoAuthenticationInformation")); + assertTrue(e.getServiceMessage().contains("Server failed to authenticate the request. Please refer to the information in the www-authenticate header.")); + }); + }*/ + @RequiredServiceVersion(clazz = ShareServiceVersion.class, min = "2021-04-10") @Test public void uploadRangeFromURLOAuth() { @@ -747,7 +762,7 @@ public void startCopyWithArgs(boolean setFilePermissionKey, boolean setFilePermi boolean setArchiveAttribute, PermissionCopyModeType permissionType) { primaryFileAsyncClient.create(1024).block(); String sourceURL = primaryFileAsyncClient.getFileUrl(); - String filePermissionKey = shareClient.createPermission(FILE_PERMISSION); + String filePermissionKey = shareAsyncClient.createPermission(FILE_PERMISSION).block(); // We recreate file properties for each test since we need to store the times for the test with // testResourceNamer.now() smbProperties.setFileCreationTime(testResourceNamer.now()) @@ -778,6 +793,23 @@ public void startCopyError() { .verify(Duration.ofMinutes(1)); } + /*@RequiredServiceVersion(clazz = ShareServiceVersion.class, min = "2024-08-04") + @Test + public void startCopySourceErrorAndStatusCode() { + primaryFileAsyncClient.create(1024); + + PollerFlux poller = setPlaybackPollerFluxPollInterval( + primaryFileAsyncClient.beginCopy("https://error.file.core.windows.net/garbage", new ShareFileCopyOptions(), null)); + + StepVerifier.create(primaryFileAsyncClient.create(1024).thenMany(poller)) + .verifyErrorSatisfies(r -> { + ShareStorageException e = assertInstanceOf(ShareStorageException.class, r); + assertTrue(e.getStatusCode() == 400); + assertTrue(e.getServiceMessage().contains("InvalidUri")); + assertTrue(e.getServiceMessage().contains("The requested URI does not represent any resource on the server.")); + }); + }*/ + @Disabled("There is a race condition in Poller where it misses the first observed event if there is a gap " + "between the time subscribed and the time we start observing events.") @Test @@ -819,7 +851,7 @@ public void startCopyWithOptions(boolean setFilePermissionKey, boolean setFilePe boolean setArchiveAttribute, PermissionCopyModeType permissionType) { primaryFileAsyncClient.create(1024).block(); String sourceURL = primaryFileAsyncClient.getFileUrl(); - String filePermissionKey = shareClient.createPermission(FILE_PERMISSION); + String filePermissionKey = shareAsyncClient.createPermission(FILE_PERMISSION).block(); // We recreate file properties for each test since we need to store the times for the test with // testResourceNamer.now() smbProperties.setFileCreationTime(testResourceNamer.now()) @@ -920,7 +952,7 @@ public void startCopyWithOptionsChangeTime() { public void startCopyWithOptionsCopySmbFilePropertiesPermissionKey() { primaryFileAsyncClient.create(1024).block(); String sourceURL = primaryFileAsyncClient.getFileUrl(); - String filePermissionKey = shareClient.createPermission(FILE_PERMISSION); + String filePermissionKey = shareAsyncClient.createPermission(FILE_PERMISSION).block(); EnumSet ntfs = EnumSet.of(NtfsFileAttributes.READ_ONLY, NtfsFileAttributes.ARCHIVE); // We recreate file properties for each test since we need to store the times for the test with // testResourceNamer.now() @@ -1187,7 +1219,7 @@ public void getPropertiesError() { @Test public void setHttpHeadersFpk() { primaryFileAsyncClient.createWithResponse(1024, null, null, null, null).block(); - String filePermissionKey = shareClient.createPermission(FILE_PERMISSION); + String filePermissionKey = shareAsyncClient.createPermission(FILE_PERMISSION).block(); smbProperties.setFileCreationTime(testResourceNamer.now()) .setFileLastWriteTime(testResourceNamer.now()) .setFilePermissionKey(filePermissionKey); @@ -1540,7 +1572,7 @@ public void storageAccountAudience() { ShareServiceAsyncClient oAuthServiceClient = getOAuthServiceAsyncClient(new ShareServiceClientBuilder() .shareTokenIntent(ShareTokenIntent.BACKUP) - .audience(ShareAudience.createShareServiceAccountAudience(shareClient.getAccountName()))); + .audience(ShareAudience.createShareServiceAccountAudience(shareAsyncClient.getAccountName()))); ShareFileAsyncClient aadFileClient = oAuthServiceClient.getShareAsyncClient(shareName).getFileClient(fileName); @@ -1570,7 +1602,7 @@ public void audienceError() { @Test public void audienceFromString() { - String url = String.format("https://%s.file.core.windows.net/", shareClient.getAccountName()); + String url = String.format("https://%s.file.core.windows.net/", shareAsyncClient.getAccountName()); ShareAudience audience = ShareAudience.fromString(url); String fileName = generatePathName(); diff --git a/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/FileServiceApiTests.java b/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/FileServiceApiTests.java index f6f18b8843498..6994288d30b69 100644 --- a/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/FileServiceApiTests.java +++ b/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/FileServiceApiTests.java @@ -7,8 +7,10 @@ import com.azure.core.http.rest.Response; import com.azure.core.util.Context; import com.azure.storage.common.StorageSharedKeyCredential; +import com.azure.storage.common.implementation.Constants; import com.azure.storage.common.test.shared.extensions.PlaybackOnly; import com.azure.storage.common.test.shared.extensions.RequiredServiceVersion; +import com.azure.storage.file.share.implementation.util.ModelHelper; import com.azure.storage.file.share.models.ListSharesOptions; import com.azure.storage.file.share.models.ShareAccessTier; import com.azure.storage.file.share.models.ShareCorsRule; @@ -17,6 +19,7 @@ import com.azure.storage.file.share.models.ShareMetrics; import com.azure.storage.file.share.models.ShareProperties; import com.azure.storage.file.share.models.ShareProtocolSettings; +import com.azure.storage.file.share.models.ShareProtocols; import com.azure.storage.file.share.models.ShareRetentionPolicy; import com.azure.storage.file.share.models.ShareServiceProperties; import com.azure.storage.file.share.models.ShareSmbSettings; @@ -415,4 +418,20 @@ public void perCallPolicy() { Response response = serviceClient.getPropertiesWithResponse(null, null); assertEquals(response.getHeaders().getValue(X_MS_VERSION), "2017-11-09"); } + + @RequiredServiceVersion(clazz = ShareServiceVersion.class, min = "2024-08-04") + @Test + public void listSharesEnableSnapshotVirtualDirectoryAccess() { + ShareCreateOptions options = new ShareCreateOptions(); + ShareProtocols protocols = ModelHelper.parseShareProtocols(Constants.HeaderConstants.NFS_PROTOCOL); + options.setProtocols(protocols); + options.setSnapshotVirtualDirectoryAccessEnabled(true); + + ShareClient shareClient = premiumFileServiceClient.getShareClient(generateShareName()); + shareClient.createWithResponse(options, null, null); + + ShareItem share = premiumFileServiceClient.listShares().iterator().next(); + assertEquals(protocols.toString(), share.getProperties().getProtocols().toString()); + assertTrue(share.getProperties().isSnapshotVirtualDirectoryAccessEnabled()); + } } diff --git a/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/FileServiceAsyncApiTests.java b/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/FileServiceAsyncApiTests.java index 0faf3f05f67ee..6cbb23c90f6ab 100644 --- a/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/FileServiceAsyncApiTests.java +++ b/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/FileServiceAsyncApiTests.java @@ -6,15 +6,19 @@ import com.azure.core.http.rest.Response; import com.azure.core.util.Context; import com.azure.storage.common.StorageSharedKeyCredential; +import com.azure.storage.common.implementation.Constants; import com.azure.storage.common.test.shared.extensions.RequiredServiceVersion; +import com.azure.storage.file.share.implementation.util.ModelHelper; import com.azure.storage.file.share.models.ListSharesOptions; import com.azure.storage.file.share.models.ShareCorsRule; import com.azure.storage.file.share.models.ShareErrorCode; import com.azure.storage.file.share.models.ShareItem; import com.azure.storage.file.share.models.ShareMetrics; import com.azure.storage.file.share.models.ShareProperties; +import com.azure.storage.file.share.models.ShareProtocols; import com.azure.storage.file.share.models.ShareRetentionPolicy; import com.azure.storage.file.share.models.ShareServiceProperties; +import com.azure.storage.file.share.options.ShareCreateOptions; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -300,4 +304,25 @@ public void restoreShareError() { .verifyErrorSatisfies(it -> FileShareTestHelper.assertExceptionStatusCodeAndMessage(it, 404, ShareErrorCode.SHARE_NOT_FOUND)); } + + @RequiredServiceVersion(clazz = ShareServiceVersion.class, min = "2024-08-04") + @Test + public void listSharesEnableSnapshotVirtualDirectoryAccess() { + ShareCreateOptions options = new ShareCreateOptions(); + ShareProtocols protocols = ModelHelper.parseShareProtocols(Constants.HeaderConstants.NFS_PROTOCOL); + options.setProtocols(protocols); + options.setSnapshotVirtualDirectoryAccessEnabled(true); + + ShareAsyncClient shareClient = premiumFileServiceAsyncClient.getShareAsyncClient(generateShareName()); + shareClient.createWithResponse(options).block(); + + StepVerifier.create(premiumFileServiceAsyncClient.listShares()) + .assertNext(r -> { + ShareProperties properties = r.getProperties(); + assertEquals(protocols.toString(), properties.getProtocols().toString()); + assertTrue(properties.isSnapshotVirtualDirectoryAccessEnabled()); + }) + .thenConsumeWhile(x -> true) + .verifyComplete(); + } } diff --git a/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/ShareApiTests.java b/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/ShareApiTests.java index eee1cc9767e39..7878bf426a1be 100644 --- a/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/ShareApiTests.java +++ b/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/ShareApiTests.java @@ -154,6 +154,20 @@ public void createShare() { 201); } + @RequiredServiceVersion(clazz = ShareServiceVersion.class, min = "2024-08-04") + @Test + public void createShareSasError() { + ShareServiceClient unauthorizedServiceClient = fileServiceBuilderHelper() + .sasToken("sig=dummyToken") + .buildClient(); + + ShareClient share = unauthorizedServiceClient.getShareClient(generateShareName()); + + ShareStorageException e = assertThrows(ShareStorageException.class, share::create); + assertEquals(ShareErrorCode.AUTHENTICATION_FAILED, e.getErrorCode()); + assertTrue(e.getServiceMessage().contains("AuthenticationErrorDetail")); + } + @RequiredServiceVersion(clazz = ShareServiceVersion.class, min = "2019-12-12") @ParameterizedTest @MethodSource("createShareWithArgsSupplier") @@ -1239,4 +1253,55 @@ public void audienceFromString() { String infoPermission = aadShareClient.createPermission(permission); assertNotNull(infoPermission); } + + @RequiredServiceVersion(clazz = ShareServiceVersion.class, min = "2024-05-04") + @ParameterizedTest + @MethodSource("createEnableSnapshotVirtualDirectoryAccessSupplier") + public void createEnableSnapshotVirtualDirectoryAccess(Boolean enableSnapshotVirtualDirectoryAccess) { + ShareCreateOptions options = new ShareCreateOptions(); + ShareProtocols protocols = ModelHelper.parseShareProtocols(Constants.HeaderConstants.NFS_PROTOCOL); + options.setProtocols(protocols); + options.setSnapshotVirtualDirectoryAccessEnabled(enableSnapshotVirtualDirectoryAccess); + + premiumFileServiceClient.getShareClient(shareName).createWithResponse(options, null, null); + + ShareProperties response = premiumFileServiceClient.getShareClient(shareName).getProperties(); + assertEquals(protocols.toString(), response.getProtocols().toString()); + if (enableSnapshotVirtualDirectoryAccess == null || enableSnapshotVirtualDirectoryAccess) { + assertTrue(response.isSnapshotVirtualDirectoryAccessEnabled()); + } else { + assertFalse(response.isSnapshotVirtualDirectoryAccessEnabled()); + } + } + + private static Stream createEnableSnapshotVirtualDirectoryAccessSupplier() { + return Stream.of( + Arguments.of(true), + Arguments.of(false), + Arguments.of((Boolean) null)); + } + + @RequiredServiceVersion(clazz = ShareServiceVersion.class, min = "2024-08-04") + @ParameterizedTest + @MethodSource("createEnableSnapshotVirtualDirectoryAccessSupplier") + public void setPropertiesEnableSnapshotVirtualDirectoryAccess(Boolean enableSnapshotVirtualDirectoryAccess) { + ShareCreateOptions options = new ShareCreateOptions(); + ShareProtocols protocols = ModelHelper.parseShareProtocols(Constants.HeaderConstants.NFS_PROTOCOL); + options.setProtocols(protocols); + + premiumFileServiceClient.getShareClient(shareName).createWithResponse(options, null, null); + + ShareSetPropertiesOptions setPropertiesOptions = new ShareSetPropertiesOptions(); + setPropertiesOptions.setSnapshotVirtualDirectoryAccessEnabled(enableSnapshotVirtualDirectoryAccess); + + premiumFileServiceClient.getShareClient(shareName).setProperties(setPropertiesOptions); + + ShareProperties response = premiumFileServiceClient.getShareClient(shareName).getProperties(); + assertEquals(protocols.toString(), response.getProtocols().toString()); + if (enableSnapshotVirtualDirectoryAccess == null || enableSnapshotVirtualDirectoryAccess) { + assertTrue(response.isSnapshotVirtualDirectoryAccessEnabled()); + } else { + assertFalse(response.isSnapshotVirtualDirectoryAccessEnabled()); + } + } } diff --git a/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/ShareAsyncApiTests.java b/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/ShareAsyncApiTests.java index d12878e9615dc..9caaff8516200 100644 --- a/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/ShareAsyncApiTests.java +++ b/sdk/storage/azure-storage-file-share/src/test/java/com/azure/storage/file/share/ShareAsyncApiTests.java @@ -5,7 +5,9 @@ import com.azure.core.http.rest.Response; import com.azure.storage.common.StorageSharedKeyCredential; +import com.azure.storage.common.implementation.Constants; import com.azure.storage.common.test.shared.extensions.PlaybackOnly; +import com.azure.storage.common.test.shared.extensions.RequiredServiceVersion; import com.azure.storage.file.share.implementation.util.ModelHelper; import com.azure.storage.file.share.models.NtfsFileAttributes; import com.azure.storage.file.share.models.ShareAudience; @@ -88,6 +90,23 @@ public void createShare() { .assertNext(it -> FileShareTestHelper.assertResponseStatusCode(it, 201)).verifyComplete(); } + @RequiredServiceVersion(clazz = ShareServiceVersion.class, min = "2024-08-04") + @Test + public void createShareSasError() { + ShareServiceAsyncClient unauthorizedServiceClient = fileServiceBuilderHelper() + .sasToken("sig=dummyToken") + .buildAsyncClient(); + + ShareAsyncClient share = unauthorizedServiceClient.getShareAsyncClient(generateShareName()); + + StepVerifier.create(share.create()) + .verifyErrorSatisfies(r -> { + ShareStorageException e = assertInstanceOf(ShareStorageException.class, r); + assertEquals(ShareErrorCode.AUTHENTICATION_FAILED, e.getErrorCode()); + assertTrue(e.getServiceMessage().contains("AuthenticationErrorDetail")); + }); + } + @ParameterizedTest @MethodSource("createShareWithArgsSupplier") public void createShareWithArgs(Map metadata, Integer quota) { @@ -795,4 +814,62 @@ public void audienceFromString() { .assertNext(r -> assertNotNull(r)) .verifyComplete(); } + + @RequiredServiceVersion(clazz = ShareServiceVersion.class, min = "2024-05-04") + @ParameterizedTest + @MethodSource("createEnableSnapshotVirtualDirectoryAccessSupplier") + public void createEnableSnapshotVirtualDirectoryAccess(Boolean enableSnapshotVirtualDirectoryAccess) { + ShareCreateOptions options = new ShareCreateOptions(); + ShareProtocols protocols = ModelHelper.parseShareProtocols(Constants.HeaderConstants.NFS_PROTOCOL); + options.setProtocols(protocols); + options.setSnapshotVirtualDirectoryAccessEnabled(enableSnapshotVirtualDirectoryAccess); + + premiumFileServiceAsyncClient.getShareAsyncClient(shareName).createWithResponse(options).block(); + + StepVerifier.create(premiumFileServiceAsyncClient.getShareAsyncClient(shareName).getProperties()) + .assertNext(r -> { + assertEquals(protocols.toString(), r.getProtocols().toString()); + if (enableSnapshotVirtualDirectoryAccess == null || enableSnapshotVirtualDirectoryAccess) { + assertTrue(r.isSnapshotVirtualDirectoryAccessEnabled()); + } else { + assertFalse(r.isSnapshotVirtualDirectoryAccessEnabled()); + } + }) + .verifyComplete(); + } + + private static Stream createEnableSnapshotVirtualDirectoryAccessSupplier() { + return Stream.of( + Arguments.of(true), + Arguments.of(false), + Arguments.of((Boolean) null)); + } + + @RequiredServiceVersion(clazz = ShareServiceVersion.class, min = "2024-08-04") + @ParameterizedTest + @MethodSource("createEnableSnapshotVirtualDirectoryAccessSupplier") + public void setPropertiesEnableSnapshotVirtualDirectoryAccess(Boolean enableSnapshotVirtualDirectoryAccess) { + ShareCreateOptions options = new ShareCreateOptions(); + ShareProtocols protocols = ModelHelper.parseShareProtocols(Constants.HeaderConstants.NFS_PROTOCOL); + options.setProtocols(protocols); + + premiumFileServiceAsyncClient.getShareAsyncClient(shareName).createWithResponse(options).block(); + + ShareSetPropertiesOptions setPropertiesOptions = new ShareSetPropertiesOptions(); + setPropertiesOptions.setSnapshotVirtualDirectoryAccessEnabled(enableSnapshotVirtualDirectoryAccess); + + premiumFileServiceAsyncClient.getShareAsyncClient(shareName).setProperties(setPropertiesOptions).block(); + + StepVerifier.create(premiumFileServiceAsyncClient.getShareAsyncClient(shareName).getProperties()) + .assertNext(r -> { + assertEquals(protocols.toString(), r.getProtocols().toString()); + if (enableSnapshotVirtualDirectoryAccess == null || enableSnapshotVirtualDirectoryAccess) { + assertTrue(r.isSnapshotVirtualDirectoryAccessEnabled()); + } else { + assertFalse(r.isSnapshotVirtualDirectoryAccessEnabled()); + } + }) + .verifyComplete(); + } + } diff --git a/sdk/storage/azure-storage-file-share/swagger/README.md b/sdk/storage/azure-storage-file-share/swagger/README.md index 8406399ac9687..446f77e69a183 100644 --- a/sdk/storage/azure-storage-file-share/swagger/README.md +++ b/sdk/storage/azure-storage-file-share/swagger/README.md @@ -16,7 +16,7 @@ autorest ### Code generation settings ``` yaml use: '@autorest/java@4.1.29' -input-file: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/3f9cca0301ffbb8856826d196c567d821ae190d7/specification/storage/data-plane/Microsoft.FileStorage/preview/2024-05-04/file.json +input-file: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/feature/storage/stg94base/specification/storage/data-plane/Microsoft.FileStorage/stable/2024-08-04/file.json java: true output-folder: ../ namespace: com.azure.storage.file.share diff --git a/sdk/storage/azure-storage-queue/CHANGELOG.md b/sdk/storage/azure-storage-queue/CHANGELOG.md index af87f033cd839..2e8ec2555b798 100644 --- a/sdk/storage/azure-storage-queue/CHANGELOG.md +++ b/sdk/storage/azure-storage-queue/CHANGELOG.md @@ -1,6 +1,7 @@ # Release History ## 12.22.0-beta.1 (Unreleased) +- Added support for bearer token challenges. ### Features Added diff --git a/sdk/storage/azure-storage-queue/assets.json b/sdk/storage/azure-storage-queue/assets.json index 8098acb007e06..9d98ffe0076a4 100644 --- a/sdk/storage/azure-storage-queue/assets.json +++ b/sdk/storage/azure-storage-queue/assets.json @@ -2,5 +2,5 @@ "AssetsRepo": "Azure/azure-sdk-assets", "AssetsRepoPrefixPath": "java", "TagPrefix": "java/storage/azure-storage-queue", - "Tag": "java/storage/azure-storage-queue_3f94b8446e" + "Tag": "java/storage/azure-storage-queue_762b1b570a" } diff --git a/sdk/storage/azure-storage-queue/src/main/java/com/azure/storage/queue/QueueServiceVersion.java b/sdk/storage/azure-storage-queue/src/main/java/com/azure/storage/queue/QueueServiceVersion.java index af7b97f964b16..66aabe6583ff6 100644 --- a/sdk/storage/azure-storage-queue/src/main/java/com/azure/storage/queue/QueueServiceVersion.java +++ b/sdk/storage/azure-storage-queue/src/main/java/com/azure/storage/queue/QueueServiceVersion.java @@ -117,7 +117,12 @@ public enum QueueServiceVersion implements ServiceVersion { /** * Service version {@code 2024-05-04}. */ - V2024_05_04("2024-05-04"); + V2024_05_04("2024-05-04"), + + /** + * Service version {@code 2024-08-04}. + */ + V2024_08_04("2024-08-04"); private final String version; @@ -139,6 +144,6 @@ public String getVersion() { * @return the latest {@link QueueServiceVersion} */ public static QueueServiceVersion getLatest() { - return V2024_05_04; + return V2024_08_04; } } diff --git a/sdk/storage/azure-storage-queue/src/main/java/com/azure/storage/queue/implementation/util/BuilderHelper.java b/sdk/storage/azure-storage-queue/src/main/java/com/azure/storage/queue/implementation/util/BuilderHelper.java index 585cf86259e57..17f3f28944494 100644 --- a/sdk/storage/azure-storage-queue/src/main/java/com/azure/storage/queue/implementation/util/BuilderHelper.java +++ b/sdk/storage/azure-storage-queue/src/main/java/com/azure/storage/queue/implementation/util/BuilderHelper.java @@ -12,7 +12,6 @@ import com.azure.core.http.policy.AddDatePolicy; import com.azure.core.http.policy.AddHeadersPolicy; import com.azure.core.http.policy.AzureSasCredentialPolicy; -import com.azure.core.http.policy.BearerTokenAuthenticationPolicy; import com.azure.core.http.policy.HttpLogOptions; import com.azure.core.http.policy.HttpLoggingPolicy; import com.azure.core.http.policy.HttpPipelinePolicy; @@ -36,6 +35,7 @@ import com.azure.storage.common.policy.RequestRetryOptions; import com.azure.storage.common.policy.ResponseValidationPolicyBuilder; import com.azure.storage.common.policy.ScrubEtagPolicy; +import com.azure.storage.common.policy.StorageBearerTokenChallengeAuthorizationPolicy; import com.azure.storage.common.policy.StorageSharedKeyCredentialPolicy; import com.azure.storage.common.sas.CommonSasQueryParameters; import com.azure.storage.queue.models.QueueAudience; @@ -199,7 +199,7 @@ public static HttpPipeline buildPipeline( httpsValidation(tokenCredential, "bearer token", endpoint, logger); String scope = audience != null ? ((audience.toString().endsWith("/") ? audience + ".default" : audience + "/.default")) : Constants.STORAGE_SCOPE; - credentialPolicy = new BearerTokenAuthenticationPolicy(tokenCredential, scope); + credentialPolicy = new StorageBearerTokenChallengeAuthorizationPolicy(tokenCredential, scope); } else if (azureSasCredential != null) { credentialPolicy = new AzureSasCredentialPolicy(azureSasCredential, false); } else if (sasToken != null) { diff --git a/sdk/storage/azure-storage-queue/src/test/java/com/azure/storage/queue/QueueApiTests.java b/sdk/storage/azure-storage-queue/src/test/java/com/azure/storage/queue/QueueApiTests.java index a82c6ac067c3f..58e813de70fbf 100644 --- a/sdk/storage/azure-storage-queue/src/test/java/com/azure/storage/queue/QueueApiTests.java +++ b/sdk/storage/azure-storage-queue/src/test/java/com/azure/storage/queue/QueueApiTests.java @@ -8,6 +8,8 @@ import com.azure.core.util.Context; import com.azure.identity.DefaultAzureCredentialBuilder; import com.azure.storage.common.StorageSharedKeyCredential; +import com.azure.storage.common.test.shared.extensions.LiveOnly; +import com.azure.storage.common.test.shared.extensions.RequiredServiceVersion; import com.azure.storage.queue.models.PeekedMessageItem; import com.azure.storage.queue.models.QueueAccessPolicy; import com.azure.storage.queue.models.QueueAudience; @@ -866,17 +868,20 @@ public void storageAccountAudience() { assertNotNull(aadQueue.getProperties()); } - + @RequiredServiceVersion(clazz = QueueServiceVersion.class, min = "2024-08-04") + @LiveOnly @Test - public void audienceError() { + /* This test tests if the bearer challenge is working properly. A bad audience is passed in, the service returns + the default audience, and the request gets retried with this default audience, making the call function as expected. + */ + public void audienceErrorBearerChallengeRetry() { queueClient.createIfNotExists(); QueueClient aadQueue = getOAuthQueueClientBuilder(primaryQueueServiceClient.getQueueServiceUrl()) .queueName(queueClient.getQueueName()) .audience(QueueAudience.createQueueServiceAccountAudience("badaudience")) .buildClient(); - QueueStorageException e = assertThrows(QueueStorageException.class, aadQueue::getProperties); - assertEquals(QueueErrorCode.INVALID_AUTHENTICATION_INFO, e.getErrorCode()); + assertNotNull(aadQueue.getProperties()); } @Test diff --git a/sdk/storage/azure-storage-queue/src/test/java/com/azure/storage/queue/QueueAsyncApiTests.java b/sdk/storage/azure-storage-queue/src/test/java/com/azure/storage/queue/QueueAsyncApiTests.java index 764f68c2c41cb..efe5e772770d6 100644 --- a/sdk/storage/azure-storage-queue/src/test/java/com/azure/storage/queue/QueueAsyncApiTests.java +++ b/sdk/storage/azure-storage-queue/src/test/java/com/azure/storage/queue/QueueAsyncApiTests.java @@ -6,13 +6,14 @@ import com.azure.core.util.BinaryData; import com.azure.identity.DefaultAzureCredentialBuilder; import com.azure.storage.common.StorageSharedKeyCredential; +import com.azure.storage.common.test.shared.extensions.LiveOnly; +import com.azure.storage.common.test.shared.extensions.RequiredServiceVersion; import com.azure.storage.queue.models.PeekedMessageItem; import com.azure.storage.queue.models.QueueAccessPolicy; import com.azure.storage.queue.models.QueueAudience; import com.azure.storage.queue.models.QueueErrorCode; import com.azure.storage.queue.models.QueueMessageItem; import com.azure.storage.queue.models.QueueSignedIdentifier; -import com.azure.storage.queue.models.QueueStorageException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -42,7 +43,6 @@ import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -861,8 +861,13 @@ public void storageAccountAudience() { .verifyComplete(); } + @RequiredServiceVersion(clazz = QueueServiceVersion.class, min = "2024-08-04") + @LiveOnly @Test - public void audienceError() { + /* This test tests if the bearer challenge is working properly. A bad audience is passed in, the service returns + the default audience, and the request gets retried with this default audience, making the call function as expected. + */ + public void audienceErrorBearerChallengeRetry() { queueAsyncClient.createIfNotExists().block(); QueueAsyncClient aadQueue = getOAuthQueueClientBuilder(primaryQueueServiceAsyncClient.getQueueServiceUrl()) .queueName(queueAsyncClient.getQueueName()) @@ -870,10 +875,8 @@ public void audienceError() { .buildAsyncClient(); StepVerifier.create(aadQueue.getProperties()) - .verifyErrorSatisfies(r -> { - QueueStorageException e = assertInstanceOf(QueueStorageException.class, r); - assertEquals(QueueErrorCode.INVALID_AUTHENTICATION_INFO, e.getErrorCode()); - }); + .assertNext(r -> assertNotNull(r)) + .verifyComplete(); } @Test diff --git a/sdk/storage/azure-storage-queue/src/test/java/com/azure/storage/queue/QueueServiceApiTests.java b/sdk/storage/azure-storage-queue/src/test/java/com/azure/storage/queue/QueueServiceApiTests.java index 48cc6533294c3..e1990aa131026 100644 --- a/sdk/storage/azure-storage-queue/src/test/java/com/azure/storage/queue/QueueServiceApiTests.java +++ b/sdk/storage/azure-storage-queue/src/test/java/com/azure/storage/queue/QueueServiceApiTests.java @@ -7,6 +7,8 @@ import com.azure.core.http.rest.PagedResponse; import com.azure.core.http.rest.Response; import com.azure.identity.DefaultAzureCredentialBuilder; +import com.azure.storage.common.test.shared.extensions.LiveOnly; +import com.azure.storage.common.test.shared.extensions.RequiredServiceVersion; import com.azure.storage.queue.models.QueueAnalyticsLogging; import com.azure.storage.queue.models.QueueAudience; import com.azure.storage.queue.models.QueueErrorCode; @@ -246,14 +248,18 @@ public void storageAccountAudience() { assertNotNull(aadService.getProperties()); } + @RequiredServiceVersion(clazz = QueueServiceVersion.class, min = "2024-08-04") + @LiveOnly @Test - public void audienceError() { + /* This test tests if the bearer challenge is working properly. A bad audience is passed in, the service returns + the default audience, and the request gets retried with this default audience, making the call function as expected. + */ + public void audienceErrorBearerChallengeRetry() { QueueServiceClient aadService = getOAuthServiceClientBuilder(primaryQueueServiceClient.getQueueServiceUrl()) .audience(QueueAudience.createQueueServiceAccountAudience("badaudience")) .buildClient(); - QueueStorageException e = assertThrows(QueueStorageException.class, aadService::getProperties); - assertEquals(QueueErrorCode.INVALID_AUTHENTICATION_INFO, e.getErrorCode()); + assertNotNull(aadService.getProperties()); } @Test diff --git a/sdk/storage/azure-storage-queue/src/test/java/com/azure/storage/queue/QueueServiceAsyncApiTests.java b/sdk/storage/azure-storage-queue/src/test/java/com/azure/storage/queue/QueueServiceAsyncApiTests.java index 38239e057f4b5..3e3868104d8b7 100644 --- a/sdk/storage/azure-storage-queue/src/test/java/com/azure/storage/queue/QueueServiceAsyncApiTests.java +++ b/sdk/storage/azure-storage-queue/src/test/java/com/azure/storage/queue/QueueServiceAsyncApiTests.java @@ -5,6 +5,8 @@ import com.azure.core.http.rest.PagedResponse; import com.azure.identity.DefaultAzureCredentialBuilder; +import com.azure.storage.common.test.shared.extensions.LiveOnly; +import com.azure.storage.common.test.shared.extensions.RequiredServiceVersion; import com.azure.storage.queue.models.QueueAnalyticsLogging; import com.azure.storage.queue.models.QueueAudience; import com.azure.storage.queue.models.QueueErrorCode; @@ -12,7 +14,6 @@ import com.azure.storage.queue.models.QueueMetrics; import com.azure.storage.queue.models.QueueRetentionPolicy; import com.azure.storage.queue.models.QueueServiceProperties; -import com.azure.storage.queue.models.QueueStorageException; import com.azure.storage.queue.models.QueuesSegmentOptions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -226,17 +227,20 @@ public void storageAccountAudience() { .verifyComplete(); } + @RequiredServiceVersion(clazz = QueueServiceVersion.class, min = "2024-08-04") + @LiveOnly @Test - public void audienceError() { + /* This test tests if the bearer challenge is working properly. A bad audience is passed in, the service returns + the default audience, and the request gets retried with this default audience, making the call function as expected. + */ + public void audienceErrorBearerChallengeRetry() { QueueServiceAsyncClient aadService = getOAuthServiceClientBuilder(primaryQueueServiceAsyncClient.getQueueServiceUrl()) .audience(QueueAudience.createQueueServiceAccountAudience("badaudience")) .buildAsyncClient(); StepVerifier.create(aadService.getProperties()) - .verifyErrorSatisfies(r -> { - QueueStorageException e = assertInstanceOf(QueueStorageException.class, r); - assertEquals(QueueErrorCode.INVALID_AUTHENTICATION_INFO, e.getErrorCode()); - }); + .assertNext(r -> assertNotNull(r)) + .verifyComplete(); } @Test