BlobServiceClient::CreateBlobContainer/DeleteBlobContainer/UndeleteBl… (#1324)

* BlobServiceClient::CreateBlobContainer/DeleteBlobContainer/UndeleteBlobContainer

* add test

* BlobContainerClient::DeleteBlob

* changelog

* fix build error

* fix build errors

* use response<void>

* fix ut
This commit is contained in:
JinmingHu 2021-01-13 13:51:28 +08:00 committed by GitHub
parent 4f1c0850fb
commit e0e04cfee0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 193 additions and 89 deletions

View File

@ -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)

View File

@ -164,20 +164,6 @@ namespace Azure { namespace Storage { namespace Blobs {
Azure::Core::Response<Models::DeleteBlobContainerResult> 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<Models::UndeleteBlobContainerResult> 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<Models::BreakBlobContainerLeaseResult> 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<void> DeleteBlob(
const std::string& blobName,
const DeleteBlobOptions& options = DeleteBlobOptions()) const;
private:
Azure::Core::Http::Url m_blobContainerUrl;
std::shared_ptr<Azure::Core::Http::HttpPipeline> m_pipeline;

View File

@ -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<std::string> DestinationBlobContainerName;
};
/**

View File

@ -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<BlobContainerClient> 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<void> 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<BlobContainerClient> UndeleteBlobContainer(
const std::string deletedBlobContainerName,
const std::string deletedBlobContainerVersion,
const UndeleteBlobContainerOptions& options = UndeleteBlobContainerOptions()) const;
private:
Azure::Core::Http::Url m_serviceUrl;
std::shared_ptr<Azure::Core::Http::HttpPipeline> m_pipeline;

View File

@ -204,18 +204,6 @@ namespace Azure { namespace Storage { namespace Blobs {
}
}
Azure::Core::Response<Models::UndeleteBlobContainerResult> 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<Models::GetBlobContainerPropertiesResult>
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<void> BlobContainerClient::DeleteBlob(
const std::string& blobName,
const DeleteBlobOptions& options) const
{
auto blobClient = GetBlobClient(blobName);
auto response = blobClient.Delete(options);
return Azure::Core::Response<void>(response.ExtractRawResponse());
}
}}} // namespace Azure::Storage::Blobs

View File

@ -192,4 +192,46 @@ namespace Azure { namespace Storage { namespace Blobs {
options.Context, *m_pipeline, m_serviceUrl, protocolLayerOptions);
}
Azure::Core::Response<BlobContainerClient> BlobServiceClient::CreateBlobContainer(
const std::string& blobContainerName,
const CreateBlobContainerOptions& options) const
{
auto blobContainerClient = GetBlobContainerClient(blobContainerName);
auto response = blobContainerClient.Create(options);
return Azure::Core::Response<BlobContainerClient>(
std::move(blobContainerClient), response.ExtractRawResponse());
}
Azure::Core::Response<void> BlobServiceClient::DeleteBlobContainer(
const std::string& blobContainerName,
const DeleteBlobContainerOptions& options) const
{
auto blobContainerClient = GetBlobContainerClient(blobContainerName);
auto response = blobContainerClient.Delete(options);
return Azure::Core::Response<void>(response.ExtractRawResponse());
}
Azure::Core::Response<BlobContainerClient> 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<BlobContainerClient>(
std::move(blobContainerClient), response.ExtractRawResponse());
}
}}} // namespace Azure::Storage::Blobs

View File

@ -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

View File

@ -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