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:
parent
4f1c0850fb
commit
e0e04cfee0
@ -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)
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user