diff --git a/sdk/storage/azure-storage-blob/CHANGELOG.md b/sdk/storage/azure-storage-blob/CHANGELOG.md index dbd281841bde4..b29a201477b85 100644 --- a/sdk/storage/azure-storage-blob/CHANGELOG.md +++ b/sdk/storage/azure-storage-blob/CHANGELOG.md @@ -3,6 +3,7 @@ ## 12.9.0-beta.1 (Unreleased) - Added support for the 2019-02-10 service version. - Added support to specify Arrow Output Serialization when querying a blob. +- Added support to undelete a container. - Fixed a bug where the TokenCredential scope would be incorrect for custom URLs. - Fixed a bug where Default Azure Credential would not work with Azurite. 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 15fc84ebd1d75..25902b48fdbe5 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 @@ -36,6 +36,7 @@ import com.azure.storage.blob.models.PublicAccessType; import com.azure.storage.blob.models.StorageAccountInfo; import com.azure.storage.blob.models.UserDelegationKey; +import com.azure.storage.blob.options.UndeleteBlobContainerOptions; import com.azure.storage.common.StorageSharedKeyCredential; import com.azure.storage.common.implementation.AccountSasImplUtil; import com.azure.storage.common.implementation.Constants; @@ -428,15 +429,12 @@ private Mono> findBlobsByTags( */ private List toIncludeTypes(BlobContainerListDetails blobContainerListDetails) { boolean hasDetails = blobContainerListDetails != null - && blobContainerListDetails.getRetrieveMetadata(); - // Add back for container soft delete. -// boolean hasDetails = blobContainerListDetails != null -// && (blobContainerListDetails.getRetrieveMetadata() || blobContainerListDetails.getRetrieveDeleted()); + && (blobContainerListDetails.getRetrieveMetadata() || blobContainerListDetails.getRetrieveDeleted()); if (hasDetails) { List flags = new ArrayList<>(2); -// if (blobContainerListDetails.getRetrieveDeleted()) { -// flags.add(ListBlobContainersIncludeType.DELETED); -// } + if (blobContainerListDetails.getRetrieveDeleted()) { + flags.add(ListBlobContainersIncludeType.DELETED); + } if (blobContainerListDetails.getRetrieveMetadata()) { flags.add(ListBlobContainersIncludeType.METADATA); } @@ -849,14 +847,13 @@ private void throwOnAnonymousAccess() { * @return A {@link Mono} containing a {@link BlobContainerAsyncClient} used * to interact with the restored container. */ - /* public Mono undeleteBlobContainer( String deletedContainerName, String deletedContainerVersion) { - return this.undeleteBlobContainerWithResponse( - new UndeleteBlobContainerOptions(deletedContainerName, deletedContainerVersion) + return this.undeleteBlobContainerWithResponse(new UndeleteBlobContainerOptions(deletedContainerName, + deletedContainerVersion) ).flatMap(FluxUtil::toMono); } - */ + /** * Restores a previously deleted container. The restored container @@ -877,13 +874,10 @@ public Mono undeleteBlobContainer( * @return A {@link Mono} containing a {@link Response} whose {@link Response#getValue() value} contains a {@link * BlobContainerAsyncClient} used to interact with the restored container. */ - /* public Mono> undeleteBlobContainerWithResponse( UndeleteBlobContainerOptions options) { try { - return withContext(context -> - undeleteBlobContainerWithResponse( - options, context)); + return withContext(context -> undeleteBlobContainerWithResponse(options, context)); } catch (RuntimeException ex) { return monoError(logger, ex); } @@ -903,5 +897,5 @@ Mono> undeleteBlobContainerWithResponse( .map(response -> new SimpleResponse<>(response, getBlobContainerAsyncClient(finalDestinationContainerName))); } - */ + } diff --git a/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/BlobServiceClient.java b/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/BlobServiceClient.java index 52d3e76c6e29b..c87a41eae1325 100644 --- a/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/BlobServiceClient.java +++ b/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/BlobServiceClient.java @@ -19,6 +19,7 @@ import com.azure.storage.blob.models.PublicAccessType; import com.azure.storage.blob.models.StorageAccountInfo; import com.azure.storage.blob.models.UserDelegationKey; +import com.azure.storage.blob.options.UndeleteBlobContainerOptions; import com.azure.storage.common.StorageSharedKeyCredential; import com.azure.storage.common.implementation.StorageImplUtils; import com.azure.storage.common.sas.AccountSasSignatureValues; @@ -450,13 +451,11 @@ public String generateAccountSas(AccountSasSignatureValues accountSasSignatureVa * @param deletedContainerVersion The version of the previously deleted container. * @return The {@link BlobContainerClient} used to interact with the restored container. */ - /* public BlobContainerClient undeleteBlobContainer(String deletedContainerName, String deletedContainerVersion) { return this.undeleteBlobContainerWithResponse( new UndeleteBlobContainerOptions(deletedContainerName, deletedContainerVersion), null, Context.NONE).getValue(); } - */ /** * Restores a previously deleted container. The restored container @@ -479,14 +478,12 @@ public BlobContainerClient undeleteBlobContainer(String deletedContainerName, St * @return A {@link Response} whose {@link Response#getValue() value} contains the {@link BlobContainerClient} used * to interact with the restored container. */ - /* public Response undeleteBlobContainerWithResponse( UndeleteBlobContainerOptions options, Duration timeout, Context context) { Mono> response = this.blobServiceAsyncClient.undeleteBlobContainerWithResponse(options, context) .map(r -> new SimpleResponse<>(r, getBlobContainerClient(r.getValue().getBlobContainerName()))); - return blockWithOptionalTimeout(response, timeout); + return StorageImplUtils.blockWithOptionalTimeout(response, timeout); } - */ } diff --git a/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/models/BlobContainerListDetails.java b/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/models/BlobContainerListDetails.java index 69c07a369fd00..b582225f215e8 100644 --- a/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/models/BlobContainerListDetails.java +++ b/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/models/BlobContainerListDetails.java @@ -14,7 +14,7 @@ @Fluent public final class BlobContainerListDetails { private boolean retrieveMetadata; - // private boolean retrieveDeleted; + private boolean retrieveDeleted; /** * Constructs an unpopulated {@link BlobContainerListDetails}. @@ -47,11 +47,9 @@ public BlobContainerListDetails setRetrieveMetadata(boolean retrieveMetadata) { * * @return a flag indicating whether deleted containers should be returned */ - /* - private boolean getRetrieveDeleted() { + public boolean getRetrieveDeleted() { return this.retrieveDeleted; } - */ /** * Whether deleted containers should be returned. @@ -59,12 +57,10 @@ private boolean getRetrieveDeleted() { * @param retrieveDeleted Flag indicating whether deleted containers should be returned. * @return the updated ContainerListDetails object */ - /* - private BlobContainerListDetails setRetrieveDeleted(boolean retrieveDeleted) { + public BlobContainerListDetails setRetrieveDeleted(boolean retrieveDeleted) { this.retrieveDeleted = retrieveDeleted; return this; } - */ /** * @return the listing flags diff --git a/sdk/storage/azure-storage-blob/src/samples/java/com/azure/storage/blob/BlobServiceAsyncClientJavaDocCodeSnippets.java b/sdk/storage/azure-storage-blob/src/samples/java/com/azure/storage/blob/BlobServiceAsyncClientJavaDocCodeSnippets.java index 2c6a07eb01c8b..0221b52eab6ea 100644 --- a/sdk/storage/azure-storage-blob/src/samples/java/com/azure/storage/blob/BlobServiceAsyncClientJavaDocCodeSnippets.java +++ b/sdk/storage/azure-storage-blob/src/samples/java/com/azure/storage/blob/BlobServiceAsyncClientJavaDocCodeSnippets.java @@ -3,6 +3,7 @@ package com.azure.storage.blob; +import com.azure.core.http.rest.Response; import com.azure.core.util.Context; import com.azure.storage.blob.models.BlobAnalyticsLogging; import com.azure.storage.blob.models.BlobContainerListDetails; @@ -12,10 +13,12 @@ import com.azure.storage.blob.options.FindBlobsOptions; import com.azure.storage.blob.models.ListBlobContainersOptions; import com.azure.storage.blob.models.PublicAccessType; +import com.azure.storage.blob.options.UndeleteBlobContainerOptions; 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 reactor.core.publisher.Mono; import java.time.Duration; import java.time.OffsetDateTime; @@ -276,42 +279,41 @@ public void generateAccountSas() { // END: com.azure.storage.blob.BlobServiceAsyncClient.generateAccountSas#AccountSasSignatureValues } - // Add back for container soft delete -// /** -// * Code snippet for {@link BlobServiceAsyncClient#undeleteBlobContainer(String, String)}. -// */ -// public void undeleteBlobContainer() { -// // BEGIN: com.azure.storage.blob.BlobServiceAsyncClient.undeleteBlobContainer#String-String -// ListBlobContainersOptions listBlobContainersOptions = new ListBlobContainersOptions(); -// listBlobContainersOptions.getDetails().setRetrieveDeleted(true); -// client.listBlobContainers(listBlobContainersOptions).flatMap( -// deletedContainer -> { -// Mono blobContainerClient = client.undeleteBlobContainer( -// deletedContainer.getName(), deletedContainer.getVersion()); -// return blobContainerClient; -// } -// ).then().block(); -// // END: com.azure.storage.blob.BlobServiceAsyncClient.undeleteBlobContainer#String-String -// } -// -// /** -// * Code snippet for -// * {@link BlobServiceAsyncClient#undeleteBlobContainerWithResponse(UndeleteBlobContainerOptions)}. -// */ -// public void undeleteBlobContainerWithResponseWithRename() { -// Context context = new Context("Key", "Value"); -// // BEGIN: com.azure.storage.blob.BlobServiceAsyncClient.undeleteBlobContainerWithResponse#UndeleteBlobContainerOptions -// ListBlobContainersOptions listBlobContainersOptions = new ListBlobContainersOptions(); -// listBlobContainersOptions.getDetails().setRetrieveDeleted(true); -// client.listBlobContainers(listBlobContainersOptions).flatMap( -// deletedContainer -> { -// Mono blobContainerClient = client.undeleteBlobContainerWithResponse( -// new UndeleteBlobContainerOptions(deletedContainer.getName(), deletedContainer.getVersion()) -// .setDestinationContainerName(deletedContainer.getName() + "V2")) -// .map(Response::getValue); -// return blobContainerClient; -// } -// ).then().block(); -// // END: com.azure.storage.blob.BlobServiceAsyncClient.undeleteBlobContainerWithResponse#UndeleteBlobContainerOptions -// } + /** + * Code snippet for {@link BlobServiceAsyncClient#undeleteBlobContainer(String, String)}. + */ + public void undeleteBlobContainer() { + // BEGIN: com.azure.storage.blob.BlobServiceAsyncClient.undeleteBlobContainer#String-String + ListBlobContainersOptions listBlobContainersOptions = new ListBlobContainersOptions(); + listBlobContainersOptions.getDetails().setRetrieveDeleted(true); + client.listBlobContainers(listBlobContainersOptions).flatMap( + deletedContainer -> { + Mono blobContainerClient = client.undeleteBlobContainer( + deletedContainer.getName(), deletedContainer.getVersion()); + return blobContainerClient; + } + ).then().block(); + // END: com.azure.storage.blob.BlobServiceAsyncClient.undeleteBlobContainer#String-String + } + + /** + * Code snippet for + * {@link BlobServiceAsyncClient#undeleteBlobContainerWithResponse(UndeleteBlobContainerOptions)}. + */ + public void undeleteBlobContainerWithResponseWithRename() { + Context context = new Context("Key", "Value"); + // BEGIN: com.azure.storage.blob.BlobServiceAsyncClient.undeleteBlobContainerWithResponse#UndeleteBlobContainerOptions + ListBlobContainersOptions listBlobContainersOptions = new ListBlobContainersOptions(); + listBlobContainersOptions.getDetails().setRetrieveDeleted(true); + client.listBlobContainers(listBlobContainersOptions).flatMap( + deletedContainer -> { + Mono blobContainerClient = client.undeleteBlobContainerWithResponse( + new UndeleteBlobContainerOptions(deletedContainer.getName(), deletedContainer.getVersion()) + .setDestinationContainerName(deletedContainer.getName() + "V2")) + .map(Response::getValue); + return blobContainerClient; + } + ).then().block(); + // END: com.azure.storage.blob.BlobServiceAsyncClient.undeleteBlobContainerWithResponse#UndeleteBlobContainerOptions + } } diff --git a/sdk/storage/azure-storage-blob/src/samples/java/com/azure/storage/blob/BlobServiceClientJavaDocCodeSnippets.java b/sdk/storage/azure-storage-blob/src/samples/java/com/azure/storage/blob/BlobServiceClientJavaDocCodeSnippets.java index 700e7d1bed006..fa83af3a03a7e 100644 --- a/sdk/storage/azure-storage-blob/src/samples/java/com/azure/storage/blob/BlobServiceClientJavaDocCodeSnippets.java +++ b/sdk/storage/azure-storage-blob/src/samples/java/com/azure/storage/blob/BlobServiceClientJavaDocCodeSnippets.java @@ -13,6 +13,7 @@ import com.azure.storage.blob.models.ListBlobContainersOptions; import com.azure.storage.blob.models.PublicAccessType; import com.azure.storage.blob.models.StorageAccountInfo; +import com.azure.storage.blob.options.UndeleteBlobContainerOptions; import com.azure.storage.common.sas.AccountSasPermission; import com.azure.storage.common.sas.AccountSasResourceType; import com.azure.storage.common.sas.AccountSasService; @@ -283,40 +284,39 @@ public void generateAccountSas() { // END: com.azure.storage.blob.BlobServiceClient.generateAccountSas#AccountSasSignatureValues } - // Add back for container soft delete -// /** -// * Code snippet for {@link BlobServiceClient#undeleteBlobContainer(String, String)}. -// */ -// public void undeleteBlobContainer() { -// // BEGIN: com.azure.storage.blob.BlobServiceClient.undeleteBlobContainer#String-String -// ListBlobContainersOptions listBlobContainersOptions = new ListBlobContainersOptions(); -// listBlobContainersOptions.getDetails().setRetrieveDeleted(true); -// client.listBlobContainers(listBlobContainersOptions, null).forEach( -// deletedContainer -> { -// BlobContainerClient blobContainerClient = client.undeleteBlobContainer( -// deletedContainer.getName(), deletedContainer.getVersion()); -// } -// ); -// // END: com.azure.storage.blob.BlobServiceClient.undeleteBlobContainer#String-String -// } -// -// /** -// * Code snippet for {@link BlobServiceClient#undeleteBlobContainerWithResponse(UndeleteBlobContainerOptions, -// * Duration, Context)}. -// */ -// public void undeleteBlobContainerWithResponseWithRename() { -// Context context = new Context("Key", "Value"); -// // BEGIN: com.azure.storage.blob.BlobServiceClient.undeleteBlobContainerWithResponse#UndeleteBlobContainerOptions-Duration-Context -// ListBlobContainersOptions listBlobContainersOptions = new ListBlobContainersOptions(); -// listBlobContainersOptions.getDetails().setRetrieveDeleted(true); -// client.listBlobContainers(listBlobContainersOptions, null).forEach( -// deletedContainer -> { -// BlobContainerClient blobContainerClient = client.undeleteBlobContainerWithResponse( -// new UndeleteBlobContainerOptions(deletedContainer.getName(), deletedContainer.getVersion()) -// .setDestinationContainerName(deletedContainer.getName() + "V2"), timeout, -// context).getValue(); -// } -// ); -// // END: com.azure.storage.blob.BlobServiceClient.undeleteBlobContainerWithResponse#UndeleteBlobContainerOptions-Duration-Context -// } + /** + * Code snippet for {@link BlobServiceClient#undeleteBlobContainer(String, String)}. + */ + public void undeleteBlobContainer() { + // BEGIN: com.azure.storage.blob.BlobServiceClient.undeleteBlobContainer#String-String + ListBlobContainersOptions listBlobContainersOptions = new ListBlobContainersOptions(); + listBlobContainersOptions.getDetails().setRetrieveDeleted(true); + client.listBlobContainers(listBlobContainersOptions, null).forEach( + deletedContainer -> { + BlobContainerClient blobContainerClient = client.undeleteBlobContainer( + deletedContainer.getName(), deletedContainer.getVersion()); + } + ); + // END: com.azure.storage.blob.BlobServiceClient.undeleteBlobContainer#String-String + } + + /** + * Code snippet for {@link BlobServiceClient#undeleteBlobContainerWithResponse(UndeleteBlobContainerOptions, + * Duration, Context)}. + */ + public void undeleteBlobContainerWithResponseWithRename() { + Context context = new Context("Key", "Value"); + // BEGIN: com.azure.storage.blob.BlobServiceClient.undeleteBlobContainerWithResponse#UndeleteBlobContainerOptions-Duration-Context + ListBlobContainersOptions listBlobContainersOptions = new ListBlobContainersOptions(); + listBlobContainersOptions.getDetails().setRetrieveDeleted(true); + client.listBlobContainers(listBlobContainersOptions, null).forEach( + deletedContainer -> { + BlobContainerClient blobContainerClient = client.undeleteBlobContainerWithResponse( + new UndeleteBlobContainerOptions(deletedContainer.getName(), deletedContainer.getVersion()) + .setDestinationContainerName(deletedContainer.getName() + "V2"), timeout, + context).getValue(); + } + ); + // END: com.azure.storage.blob.BlobServiceClient.undeleteBlobContainerWithResponse#UndeleteBlobContainerOptions-Duration-Context + } } diff --git a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/ServiceAPITest.groovy b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/ServiceAPITest.groovy index 57cfc78e6826e..3898eddcca1b1 100644 --- a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/ServiceAPITest.groovy +++ b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/ServiceAPITest.groovy @@ -161,7 +161,6 @@ class ServiceAPITest extends APISpec { containers.each { container -> container.delete() } } - @Ignore // Container soft delete def "List deleted"() { given: def NUM_CONTAINERS = 5 @@ -188,7 +187,6 @@ class ServiceAPITest extends APISpec { listResult.size() == NUM_CONTAINERS } - @Ignore // Container soft delete def "List with all details"() { given: def NUM_CONTAINERS = 5 @@ -739,7 +737,6 @@ class ServiceAPITest extends APISpec { thrown(IllegalArgumentException) } - @Ignore // Container soft delete def "Restore Container"() { given: def cc1 = primaryBlobServiceClient.getBlobContainerClient(generateContainerName()) @@ -766,7 +763,6 @@ class ServiceAPITest extends APISpec { restoredContainerClient.listBlobs().first().getName() == blobName } - @Ignore // Container soft delete def "Restore Container into other container"() { given: def cc1 = primaryBlobServiceClient.getBlobContainerClient(generateContainerName()) @@ -795,7 +791,6 @@ class ServiceAPITest extends APISpec { restoredContainerClient.listBlobs().first().getName() == blobName } - @Ignore // Container soft delete def "Restore Container with response"() { given: def cc1 = primaryBlobServiceClient.getBlobContainerClient(generateContainerName()) @@ -826,7 +821,6 @@ class ServiceAPITest extends APISpec { restoredContainerClient.listBlobs().first().getName() == blobName } - @Ignore // Container soft delete def "Restore Container async"() { given: def cc1 = primaryBlobServiceAsyncClient.getBlobContainerAsyncClient(generateContainerName()) @@ -857,7 +851,6 @@ class ServiceAPITest extends APISpec { .verifyComplete() } - @Ignore // Container soft delete def "Restore Container async with response"() { given: def cc1 = primaryBlobServiceAsyncClient.getBlobContainerAsyncClient(generateContainerName()) @@ -891,7 +884,6 @@ class ServiceAPITest extends APISpec { .verifyComplete() } - @Ignore // Container soft delete def "Restore Container error"() { when: primaryBlobServiceClient.undeleteBlobContainer(generateContainerName(), "01D60F8BB59A4652") @@ -900,7 +892,6 @@ class ServiceAPITest extends APISpec { thrown(BlobStorageException.class) } - @Ignore // Container soft delete def "Restore Container into existing container error"() { given: def cc1 = primaryBlobServiceClient.getBlobContainerClient(generateContainerName())