diff --git a/sdk/storage/azure-storage-blobs/CHANGELOG.md b/sdk/storage/azure-storage-blobs/CHANGELOG.md index 05fc861e7..f46a90c6a 100644 --- a/sdk/storage/azure-storage-blobs/CHANGELOG.md +++ b/sdk/storage/azure-storage-blobs/CHANGELOG.md @@ -7,6 +7,8 @@ - `CreateIfNotExists` and `DeleteIfExists` for blob containers and blobs. - Add `IsHierarchicalNamespaceEnabled` in `GetAccountInfoResult`. - New API: `PageBlobClient::GetPageRangesDiff` and `PageBlobClient::GetManagedDiskPageRangesDiff`. +- Add `CreateBlobContainer`, `DeleteBlobContainer`, `UndeleteBlobContainer` into `BlobServiceClient`. +- Add `DeleteBlob` into `BlobContainerClient`. ### Breaking Changes @@ -67,6 +69,7 @@ - Rename `SourceConditions` in API options to `SourceAccessConditions`. - Remove Blob Batch. - `DownloadBlobResult::Content-Range` is changed to an `Azure::Core::Http::Range`, an extra field `BlobSize` is added. +- Remove `Undelete` from `BlobContainerClient`. ## 12.0.0-beta.5 (2020-11-13) diff --git a/sdk/storage/azure-storage-blobs/inc/azure/storage/blobs/blob_container_client.hpp b/sdk/storage/azure-storage-blobs/inc/azure/storage/blobs/blob_container_client.hpp index 1a57bd255..871d63e2b 100644 --- a/sdk/storage/azure-storage-blobs/inc/azure/storage/blobs/blob_container_client.hpp +++ b/sdk/storage/azure-storage-blobs/inc/azure/storage/blobs/blob_container_client.hpp @@ -164,20 +164,6 @@ namespace Azure { namespace Storage { namespace Blobs { Azure::Core::Response DeleteIfExists( const DeleteBlobContainerOptions& options = DeleteBlobContainerOptions()) const; - /** - * @brief Restores a previously deleted container. The destionation is referenced by current - * BlobContainerClient. - * - * @param deletedBlobContainerName The name of the previously deleted container. - * @param deletedBlobContainerVersion The version of the previously deleted container. - * @param options Optional parameters to execute this function. - * @return An UndeleteBlobContainerResult if successful. - */ - Azure::Core::Response Undelete( - const std::string& deletedBlobContainerName, - const std::string& deletedBlobContainerVersion, - const UndeleteBlobContainerOptions& options = UndeleteBlobContainerOptions()) const; - /** * @brief Returns all user-defined metadata and system properties for the specified * container. The data returned does not include the container's list of blobs. @@ -325,6 +311,19 @@ namespace Azure { namespace Storage { namespace Blobs { Azure::Core::Response BreakLease( const BreakBlobContainerLeaseOptions& options = BreakBlobContainerLeaseOptions()) const; + /** + * @brief Marks the specified blob or snapshot for deletion. The blob is later deleted + * during garbage collection. Note that in order to delete a blob, you must delete all of its + * snapshots. You can delete both at the same time using DeleteBlobOptions.DeleteSnapshots. + * + * @param blobName The name of the blob to delete. + * @param options Optional parameters to execute this function. + * @return Nothing. + */ + Azure::Core::Response DeleteBlob( + const std::string& blobName, + const DeleteBlobOptions& options = DeleteBlobOptions()) const; + private: Azure::Core::Http::Url m_blobContainerUrl; std::shared_ptr m_pipeline; diff --git a/sdk/storage/azure-storage-blobs/inc/azure/storage/blobs/blob_options.hpp b/sdk/storage/azure-storage-blobs/inc/azure/storage/blobs/blob_options.hpp index b32dcd011..841fac444 100644 --- a/sdk/storage/azure-storage-blobs/inc/azure/storage/blobs/blob_options.hpp +++ b/sdk/storage/azure-storage-blobs/inc/azure/storage/blobs/blob_options.hpp @@ -330,6 +330,12 @@ namespace Azure { namespace Storage { namespace Blobs { * @brief Context for cancelling long running operations. */ Azure::Core::Context Context; + + /** + * @brief Use this parameter if you would like to restore the container under a + * different name. + */ + Azure::Core::Nullable DestinationBlobContainerName; }; /** diff --git a/sdk/storage/azure-storage-blobs/inc/azure/storage/blobs/blob_service_client.hpp b/sdk/storage/azure-storage-blobs/inc/azure/storage/blobs/blob_service_client.hpp index 70ecf356c..57222d3e1 100644 --- a/sdk/storage/azure-storage-blobs/inc/azure/storage/blobs/blob_service_client.hpp +++ b/sdk/storage/azure-storage-blobs/inc/azure/storage/blobs/blob_service_client.hpp @@ -179,6 +179,43 @@ namespace Azure { namespace Storage { namespace Blobs { const std::string& tagFilterSqlExpression, const FindBlobsByTagsSinglePageOptions& options = FindBlobsByTagsSinglePageOptions()) const; + /** + * @brief Creates a new blob container under the specified account. If the container with the + * same name already exists, the operation fails. + * + * @param blobContainerName The name of the container to create. + * @param options Optional parameters to execute this function. + * @return A BlobContainerClient referencing the newly created container. + */ + Azure::Core::Response CreateBlobContainer( + const std::string& blobContainerName, + const CreateBlobContainerOptions& options = CreateBlobContainerOptions()) const; + + /** + * @brief Marks the specified blob container for deletion. The container and any blobs + * contained within it are later deleted during garbage collection. + * + * @param blobContainerName The name of the container to delete. + * @param options Optional parameters to execute this function. + * @return Nothing. + */ + Azure::Core::Response DeleteBlobContainer( + const std::string& blobContainerName, + const DeleteBlobContainerOptions& options = DeleteBlobContainerOptions()) const; + + /** + * @brief Restores a previously deleted container. + * + * @param deletedBlobContainerName The name of the previously deleted container. + * @param deletedBlobContainerVersion The version of the previously deleted container. + * @param options Optional parameters to execute this function. + * @return A BlobContainerClient referencing the undeleted container. + */ + Azure::Core::Response UndeleteBlobContainer( + const std::string deletedBlobContainerName, + const std::string deletedBlobContainerVersion, + const UndeleteBlobContainerOptions& options = UndeleteBlobContainerOptions()) const; + private: Azure::Core::Http::Url m_serviceUrl; std::shared_ptr m_pipeline; diff --git a/sdk/storage/azure-storage-blobs/src/blob_container_client.cpp b/sdk/storage/azure-storage-blobs/src/blob_container_client.cpp index 63527d974..29032cb45 100644 --- a/sdk/storage/azure-storage-blobs/src/blob_container_client.cpp +++ b/sdk/storage/azure-storage-blobs/src/blob_container_client.cpp @@ -204,18 +204,6 @@ namespace Azure { namespace Storage { namespace Blobs { } } - Azure::Core::Response BlobContainerClient::Undelete( - const std::string& deletedBlobContainerName, - const std::string& deletedBlobContainerVersion, - const UndeleteBlobContainerOptions& options) const - { - Details::BlobRestClient::BlobContainer::UndeleteBlobContainerOptions protocolLayerOptions; - protocolLayerOptions.DeletedBlobContainerName = deletedBlobContainerName; - protocolLayerOptions.DeletedBlobContainerVersion = deletedBlobContainerVersion; - return Details::BlobRestClient::BlobContainer::Undelete( - options.Context, *m_pipeline, m_blobContainerUrl, protocolLayerOptions); - } - Azure::Core::Response BlobContainerClient::GetProperties(const GetBlobContainerPropertiesOptions& options) const { @@ -374,4 +362,13 @@ namespace Azure { namespace Storage { namespace Blobs { options.Context, *m_pipeline, m_blobContainerUrl, protocolLayerOptions); } + Azure::Core::Response BlobContainerClient::DeleteBlob( + const std::string& blobName, + const DeleteBlobOptions& options) const + { + auto blobClient = GetBlobClient(blobName); + auto response = blobClient.Delete(options); + return Azure::Core::Response(response.ExtractRawResponse()); + } + }}} // namespace Azure::Storage::Blobs diff --git a/sdk/storage/azure-storage-blobs/src/blob_service_client.cpp b/sdk/storage/azure-storage-blobs/src/blob_service_client.cpp index cb1955f5a..f97c43de6 100644 --- a/sdk/storage/azure-storage-blobs/src/blob_service_client.cpp +++ b/sdk/storage/azure-storage-blobs/src/blob_service_client.cpp @@ -192,4 +192,46 @@ namespace Azure { namespace Storage { namespace Blobs { options.Context, *m_pipeline, m_serviceUrl, protocolLayerOptions); } + Azure::Core::Response BlobServiceClient::CreateBlobContainer( + const std::string& blobContainerName, + const CreateBlobContainerOptions& options) const + { + auto blobContainerClient = GetBlobContainerClient(blobContainerName); + auto response = blobContainerClient.Create(options); + return Azure::Core::Response( + std::move(blobContainerClient), response.ExtractRawResponse()); + } + + Azure::Core::Response BlobServiceClient::DeleteBlobContainer( + const std::string& blobContainerName, + const DeleteBlobContainerOptions& options) const + { + auto blobContainerClient = GetBlobContainerClient(blobContainerName); + auto response = blobContainerClient.Delete(options); + return Azure::Core::Response(response.ExtractRawResponse()); + } + + Azure::Core::Response BlobServiceClient::UndeleteBlobContainer( + const std::string deletedBlobContainerName, + const std::string deletedBlobContainerVersion, + const UndeleteBlobContainerOptions& options) const + { + std::string destinationBlobContainerName = options.DestinationBlobContainerName.HasValue() + ? options.DestinationBlobContainerName.GetValue() + : deletedBlobContainerName; + auto blobContainerClient = GetBlobContainerClient(destinationBlobContainerName); + + Details::BlobRestClient::BlobContainer::UndeleteBlobContainerOptions protocolLayerOptions; + protocolLayerOptions.DeletedBlobContainerName = deletedBlobContainerName; + protocolLayerOptions.DeletedBlobContainerVersion = deletedBlobContainerVersion; + auto response = Details::BlobRestClient::BlobContainer::Undelete( + options.Context, + *m_pipeline, + Azure::Core::Http::Url(blobContainerClient.GetUrl()), + protocolLayerOptions); + + return Azure::Core::Response( + std::move(blobContainerClient), response.ExtractRawResponse()); + } + }}} // namespace Azure::Storage::Blobs diff --git a/sdk/storage/azure-storage-blobs/test/blob_container_client_test.cpp b/sdk/storage/azure-storage-blobs/test/blob_container_client_test.cpp index 232de3517..205ea8cc5 100644 --- a/sdk/storage/azure-storage-blobs/test/blob_container_client_test.cpp +++ b/sdk/storage/azure-storage-blobs/test/blob_container_client_test.cpp @@ -691,69 +691,6 @@ namespace Azure { namespace Storage { namespace Test { EXPECT_NO_THROW(containerClient.Delete(options)); } - TEST_F(BlobContainerClientTest, Undelete) - { - auto serviceClient = Azure::Storage::Blobs::BlobServiceClient::CreateFromConnectionString( - StandardStorageConnectionString()); - std::string containerName = LowercaseRandomString(); - auto containerClient = serviceClient.GetBlobContainerClient(containerName); - containerClient.Create(); - containerClient.Delete(); - - Blobs::Models::BlobContainerItem deletedContainerItem; - { - Azure::Storage::Blobs::ListBlobContainersSinglePageOptions options; - options.Prefix = containerName; - options.Include = Blobs::Models::ListBlobContainersIncludeItem::Deleted; - do - { - auto res = serviceClient.ListBlobContainersSinglePage(options); - options.ContinuationToken = res->ContinuationToken; - for (const auto& container : res->Items) - { - if (container.Name == containerName) - { - deletedContainerItem = container; - break; - } - } - } while (options.ContinuationToken.HasValue()); - } - EXPECT_EQ(deletedContainerItem.Name, containerName); - EXPECT_TRUE(deletedContainerItem.IsDeleted); - EXPECT_TRUE(deletedContainerItem.VersionId.HasValue()); - EXPECT_FALSE(deletedContainerItem.VersionId.GetValue().empty()); - EXPECT_TRUE(deletedContainerItem.DeletedOn.HasValue()); - EXPECT_TRUE(IsValidTime(deletedContainerItem.DeletedOn.GetValue())); - EXPECT_TRUE(deletedContainerItem.RemainingRetentionDays.HasValue()); - EXPECT_GE(deletedContainerItem.RemainingRetentionDays.GetValue(), 0); - - std::string containerName2 = LowercaseRandomString(); - auto containerClient2 = serviceClient.GetBlobContainerClient(containerName2); - for (int i = 0; i < 60; ++i) - { - try - { - containerClient2.Undelete( - deletedContainerItem.Name, deletedContainerItem.VersionId.GetValue()); - break; - } - catch (StorageException& e) - { - if (e.StatusCode == Azure::Core::Http::HttpStatusCode::Conflict - && e.ReasonPhrase == "The specified container is being deleted.") - { - std::this_thread::sleep_for(std::chrono::seconds(1)); - } - else - { - throw; - } - } - } - EXPECT_NO_THROW(containerClient2.GetProperties()); - } - TEST_F(BlobContainerClientTest, DISABLED_Tags) { std::string blobName = RandomString(); @@ -1143,4 +1080,14 @@ namespace Azure { namespace Storage { namespace Test { blobUrl, m_blobContainerClient->GetUrl() + "/" + Storage::Details::UrlEncodePath(blobName)); } + TEST_F(BlobContainerClientTest, DeleteBlob) + { + std::string blobName = RandomString(); + auto blobClient = m_blobContainerClient->GetAppendBlobClient(blobName); + blobClient.Create(); + EXPECT_NO_THROW(blobClient.GetProperties()); + blobClient.Delete(); + EXPECT_THROW(blobClient.GetProperties(), StorageException); + } + }}} // namespace Azure::Storage::Test diff --git a/sdk/storage/azure-storage-blobs/test/blob_service_client_test.cpp b/sdk/storage/azure-storage-blobs/test/blob_service_client_test.cpp index 03377d587..afa60574b 100644 --- a/sdk/storage/azure-storage-blobs/test/blob_service_client_test.cpp +++ b/sdk/storage/azure-storage-blobs/test/blob_service_client_test.cpp @@ -344,4 +344,77 @@ namespace Azure { namespace Storage { namespace Test { } } + TEST_F(BlobServiceClientTest, CreateDeleteBlobContainer) + { + std::string containerName = LowercaseRandomString(); + auto containerClient = m_blobServiceClient.CreateBlobContainer(containerName); + EXPECT_NO_THROW(containerClient->GetProperties()); + + m_blobServiceClient.DeleteBlobContainer(containerName); + EXPECT_THROW(containerClient->GetProperties(), StorageException); + } + + TEST_F(BlobServiceClientTest, UndeleteBlobContainer) + { + std::string containerName = LowercaseRandomString(); + auto containerClient = m_blobServiceClient.GetBlobContainerClient(containerName); + containerClient.Create(); + containerClient.Delete(); + + Blobs::Models::BlobContainerItem deletedContainerItem; + { + Azure::Storage::Blobs::ListBlobContainersSinglePageOptions options; + options.Prefix = containerName; + options.Include = Blobs::Models::ListBlobContainersIncludeItem::Deleted; + do + { + auto res = m_blobServiceClient.ListBlobContainersSinglePage(options); + options.ContinuationToken = res->ContinuationToken; + for (const auto& container : res->Items) + { + if (container.Name == containerName) + { + deletedContainerItem = container; + break; + } + } + } while (options.ContinuationToken.HasValue()); + } + EXPECT_EQ(deletedContainerItem.Name, containerName); + EXPECT_TRUE(deletedContainerItem.IsDeleted); + EXPECT_TRUE(deletedContainerItem.VersionId.HasValue()); + EXPECT_FALSE(deletedContainerItem.VersionId.GetValue().empty()); + EXPECT_TRUE(deletedContainerItem.DeletedOn.HasValue()); + EXPECT_TRUE(IsValidTime(deletedContainerItem.DeletedOn.GetValue())); + EXPECT_TRUE(deletedContainerItem.RemainingRetentionDays.HasValue()); + EXPECT_GE(deletedContainerItem.RemainingRetentionDays.GetValue(), 0); + + std::string containerName2 = LowercaseRandomString(); + for (int i = 0; i < 60; ++i) + { + try + { + Azure::Storage::Blobs::UndeleteBlobContainerOptions options; + options.DestinationBlobContainerName = containerName2; + m_blobServiceClient.UndeleteBlobContainer( + deletedContainerItem.Name, deletedContainerItem.VersionId.GetValue(), options); + break; + } + catch (StorageException& e) + { + if (e.StatusCode == Azure::Core::Http::HttpStatusCode::Conflict + && e.ReasonPhrase == "The specified container is being deleted.") + { + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + else + { + throw; + } + } + } + auto containerClient2 = m_blobServiceClient.GetBlobContainerClient(containerName2); + EXPECT_NO_THROW(containerClient2.GetProperties()); + } + }}} // namespace Azure::Storage::Test