CreateIfNotExists and DeleteIfExists (#1059)

* CreateIfNotExists DeleteIfExists for containers

* DeleteIfExists for blobs

* CreateIfNotExists for append blob

* CreateIfNotExists for page blob

* changelog
This commit is contained in:
JinmingHu 2020-12-03 11:19:52 +08:00 committed by GitHub
parent c3afb7798f
commit 9febf43832
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 260 additions and 17 deletions

View File

@ -2,6 +2,10 @@
## 12.0.0-beta.6 (Unreleased)
### New Features
* `CreateIfNotExists` and `DeleteIfExists` for blob containers and blobs.
### Breaking Changes
* Rename AppendBlobAccessConditions::MaxSize to AppendBlobAccessConditions::IfMaxSizeLessThanOrEqual.

View File

@ -117,6 +117,17 @@ namespace Azure { namespace Storage { namespace Blobs {
Azure::Core::Response<Models::CreateAppendBlobResult> Create(
const CreateAppendBlobOptions& options = CreateAppendBlobOptions()) const;
/**
* @brief Creates a new 0-length append blob. The content keeps unchanged if the blob already
* exists.
*
* @param options Optional parameters to execute this function.
* @return A CreateAppendBlobResult describing the newly created append blob. Null if the blob
* already exists.
*/
Azure::Core::Response<Models::CreateAppendBlobResult> CreateIfNotExists(
const CreateAppendBlobOptions& options = CreateAppendBlobOptions()) const;
/**
* @brief Commits a new block of data, represented by the content BodyStream to the end
* of the existing append blob.

View File

@ -272,6 +272,15 @@ namespace Azure { namespace Storage { namespace Blobs {
Azure::Core::Response<Models::DeleteBlobResult> Delete(
const DeleteBlobOptions& options = DeleteBlobOptions()) const;
/**
* @brief Marks the specified blob or snapshot for deletion if it exists.
*
* @param options Optional parameters to execute this function.
* @return A DeleteBlobResult on successfully deleting. Null if the blob doesn't exist.
*/
Azure::Core::Response<Models::DeleteBlobResult> DeleteIfExists(
const DeleteBlobOptions& options = DeleteBlobOptions()) const;
/**
* @brief Restores the contents and metadata of a soft deleted blob and any associated
* soft deleted snapshots.

View File

@ -129,24 +129,44 @@ namespace Azure { namespace Storage { namespace Blobs {
* @brief Creates a new container under the specified account. If the container with the
* same name already exists, the operation fails.
*
* @param options Optional
* parameters to execute this function.
* @param options Optional parameters to execute this function.
* @return A CreateBlobContainerResult describing the newly created blob container.
*/
Azure::Core::Response<Models::CreateBlobContainerResult> Create(
const CreateBlobContainerOptions& options = CreateBlobContainerOptions()) const;
/**
* @brief Creates a new container under the specified account. If the container with the
* same name already exists, it is not changed.
*
* @param options Optional parameters to execute this function.
* @return A CreateBlobContainerResult describing the newly created blob container if the
* container doesn't exist. Null if the container already exists.
*/
Azure::Core::Response<Models::CreateBlobContainerResult> CreateIfNotExists(
const CreateBlobContainerOptions& options = CreateBlobContainerOptions()) const;
/**
* @brief Marks the specified container for deletion. The container and any blobs
* contained within it are later deleted during garbage collection.
*
* @param
* options Optional parameters to execute this function.
* @param options Optional parameters to execute this function.
* @return A DeleteBlobContainerResult if successful.
*/
Azure::Core::Response<Models::DeleteBlobContainerResult> Delete(
const DeleteBlobContainerOptions& options = DeleteBlobContainerOptions()) const;
/**
* @brief Marks the specified container for deletion if it exists. The container and any blobs
* contained within it are later deleted during garbage collection.
*
* @param options Optional parameters to execute this function.
* @return A DeleteBlobContainerResult if the container exists. Null if the container doesn't
* exist.
*/
Azure::Core::Response<Models::DeleteBlobContainerResult> DeleteIfExists(
const DeleteBlobContainerOptions& options = DeleteBlobContainerOptions()) const;
/**
* @brief Restores a previously deleted container. The destionation is referenced by current
* BlobContainerClient.

View File

@ -109,12 +109,11 @@ namespace Azure { namespace Storage { namespace Blobs {
PageBlobClient WithVersionId(const std::string& versionId) const;
/**
* @brief Creates a new page blob of the specified size. The content of any existing
* @brief Creates a new page blob of the specified size. The content of any existing
* blob is overwritten with the newly initialized page blob.
*
* @param
* blobContentLength Specifies the maximum size for the page blob. The size must be aligned to a
* 512-byte boundary.
* @param blobContentLength Specifies the maximum size for the page blob. The size must be
* aligned to a 512-byte boundary.
* @param options Optional parameters to execute this function.
* @return A CreatePageBlobResult describing the newly created page blob.
*/
@ -122,6 +121,20 @@ namespace Azure { namespace Storage { namespace Blobs {
int64_t blobContentLength,
const CreatePageBlobOptions& options = CreatePageBlobOptions()) const;
/**
* @brief Creates a new page blob of the specified size. The content keeps unchanged if the blob
* already exists.
*
* @param blobContentLength Specifies the maximum size for the page blob. The size must be
* aligned to a 512-byte boundary.
* @param options Optional parameters to execute this function.
* @return A CreatePageBlobResult describing the newly created page blob. Null if the blob
* already exists.
*/
Azure::Core::Response<Models::CreatePageBlobResult> CreateIfNotExists(
int64_t blobContentLength,
const CreatePageBlobOptions& options = CreatePageBlobOptions()) const;
/**
* @brief Writes content to a range of pages in a page blob, starting at offset.
*

View File

@ -17,15 +17,8 @@ void BlobsGettingStarted()
auto containerClient
= BlobContainerClient::CreateFromConnectionString(GetConnectionString(), containerName);
try
{
containerClient.Create();
}
catch (std::runtime_error& e)
{
// The container may already exist
std::cout << e.what() << std::endl;
}
containerClient.CreateIfNotExists();
BlockBlobClient blobClient = containerClient.GetBlockBlobClient(blobName);

View File

@ -96,6 +96,26 @@ namespace Azure { namespace Storage { namespace Blobs {
options.Context, *m_pipeline, m_blobUrl, protocolLayerOptions);
}
Azure::Core::Response<Models::CreateAppendBlobResult> AppendBlobClient::CreateIfNotExists(
const CreateAppendBlobOptions& options) const
{
auto optionsCopy = options;
optionsCopy.AccessConditions.IfNoneMatch = ETagWildcard;
try
{
return Create(optionsCopy);
}
catch (StorageException& e)
{
if (e.StatusCode == Core::Http::HttpStatusCode::Conflict
&& e.ErrorCode == "BlobAlreadyExists")
{
return Azure::Core::Response<Models::CreateAppendBlobResult>(std::move(e.RawResponse));
}
throw;
}
}
Azure::Core::Response<Models::AppendBlockResult> AppendBlobClient::AppendBlock(
Azure::Core::Http::BodyStream* content,
const AppendBlockOptions& options) const

View File

@ -630,6 +630,23 @@ namespace Azure { namespace Storage { namespace Blobs {
options.Context, *m_pipeline, m_blobUrl, protocolLayerOptions);
}
Azure::Core::Response<Models::DeleteBlobResult> BlobClient::DeleteIfExists(
const DeleteBlobOptions& options) const
{
try
{
return Delete(options);
}
catch (StorageException& e)
{
if (e.StatusCode == Core::Http::HttpStatusCode::NotFound && e.ErrorCode == "BlobNotFound")
{
return Azure::Core::Response<Models::DeleteBlobResult>(std::move(e.RawResponse));
}
throw;
}
}
Azure::Core::Response<Models::UndeleteBlobResult> BlobClient::Undelete(
const UndeleteBlobOptions& options) const
{

View File

@ -151,6 +151,24 @@ namespace Azure { namespace Storage { namespace Blobs {
options.Context, *m_pipeline, m_blobContainerUrl, protocolLayerOptions);
}
Azure::Core::Response<Models::CreateBlobContainerResult> BlobContainerClient::CreateIfNotExists(
const CreateBlobContainerOptions& options) const
{
try
{
return Create(options);
}
catch (StorageException& e)
{
if (e.StatusCode == Core::Http::HttpStatusCode::Conflict
&& e.ErrorCode == "ContainerAlreadyExists")
{
return Azure::Core::Response<Models::CreateBlobContainerResult>(std::move(e.RawResponse));
}
throw;
}
}
Azure::Core::Response<Models::DeleteBlobContainerResult> BlobContainerClient::Delete(
const DeleteBlobContainerOptions& options) const
{
@ -162,6 +180,24 @@ namespace Azure { namespace Storage { namespace Blobs {
options.Context, *m_pipeline, m_blobContainerUrl, protocolLayerOptions);
}
Azure::Core::Response<Models::DeleteBlobContainerResult> BlobContainerClient::DeleteIfExists(
const DeleteBlobContainerOptions& options) const
{
try
{
return Delete(options);
}
catch (StorageException& e)
{
if (e.StatusCode == Core::Http::HttpStatusCode::NotFound
&& e.ErrorCode == "ContainerNotFound")
{
return Azure::Core::Response<Models::DeleteBlobContainerResult>(std::move(e.RawResponse));
}
throw;
}
}
Azure::Core::Response<Models::UndeleteBlobContainerResult> BlobContainerClient::Undelete(
const std::string& deletedBlobContainerName,
const std::string& deletedBlobContainerVersion,

View File

@ -102,6 +102,27 @@ namespace Azure { namespace Storage { namespace Blobs {
options.Context, *m_pipeline, m_blobUrl, protocolLayerOptions);
}
Azure::Core::Response<Models::CreatePageBlobResult> PageBlobClient::CreateIfNotExists(
int64_t blobContentLength,
const CreatePageBlobOptions& options) const
{
auto optionsCopy = options;
optionsCopy.AccessConditions.IfNoneMatch = ETagWildcard;
try
{
return Create(blobContentLength, optionsCopy);
}
catch (StorageException& e)
{
if (e.StatusCode == Core::Http::HttpStatusCode::Conflict
&& e.ErrorCode == "BlobAlreadyExists")
{
return Azure::Core::Response<Models::CreatePageBlobResult>(std::move(e.RawResponse));
}
throw;
}
}
Azure::Core::Response<Models::UploadPageBlobPagesResult> PageBlobClient::UploadPages(
int64_t offset,
Azure::Core::Http::BodyStream* content,

View File

@ -332,4 +332,27 @@ namespace Azure { namespace Storage { namespace Test {
EXPECT_TRUE(getPropertiesResult->IsSealed.GetValue());
}
TEST_F(AppendBlobClientTest, CreateIfNotExists)
{
auto blobClient = Azure::Storage::Blobs::AppendBlobClient::CreateFromConnectionString(
StandardStorageConnectionString(), m_containerName, RandomString());
auto blobClientWithoutAuth = Azure::Storage::Blobs::AppendBlobClient(blobClient.GetUrl());
EXPECT_THROW(blobClientWithoutAuth.CreateIfNotExists(), StorageException);
{
auto response = blobClient.CreateIfNotExists();
EXPECT_TRUE(response.HasValue());
}
auto blobContent
= Azure::Core::Http::MemoryBodyStream(m_blobContent.data(), m_blobContent.size());
blobClient.AppendBlock(&blobContent);
{
auto response = blobClient.CreateIfNotExists();
EXPECT_FALSE(response.HasValue());
}
auto downloadStream = std::move(blobClient.Download()->BodyStream);
EXPECT_EQ(
Azure::Core::Http::BodyStream::ReadToEnd(Azure::Core::Context(), *downloadStream),
m_blobContent);
}
}}} // namespace Azure::Storage::Test

View File

@ -70,6 +70,28 @@ namespace Azure { namespace Storage { namespace Test {
EXPECT_FALSE(res2.GetRawResponse().GetHeaders().at(Details::HttpHeaderRequestId).empty());
EXPECT_FALSE(res2.GetRawResponse().GetHeaders().at(Details::HttpHeaderDate).empty());
EXPECT_FALSE(res2.GetRawResponse().GetHeaders().at(Details::HttpHeaderXMsVersion).empty());
container_client = Azure::Storage::Blobs::BlobContainerClient::CreateFromConnectionString(
StandardStorageConnectionString(), LowercaseRandomString() + "UPPERCASE");
EXPECT_THROW(container_client.CreateIfNotExists(), StorageException);
container_client = Azure::Storage::Blobs::BlobContainerClient::CreateFromConnectionString(
StandardStorageConnectionString(), LowercaseRandomString());
{
auto response = container_client.DeleteIfExists();
EXPECT_FALSE(response.HasValue());
}
{
auto response = container_client.CreateIfNotExists();
EXPECT_TRUE(response.HasValue());
}
{
auto response = container_client.CreateIfNotExists();
EXPECT_FALSE(response.HasValue());
}
{
auto response = container_client.DeleteIfExists();
EXPECT_TRUE(response.HasValue());
}
}
TEST_F(BlobContainerClientTest, Metadata)

View File

@ -757,4 +757,34 @@ namespace Azure { namespace Storage { namespace Test {
EXPECT_TRUE(exceptionCaught);
}
TEST_F(BlockBlobClientTest, DeleteIfExists)
{
auto blobClient = Azure::Storage::Blobs::BlockBlobClient::CreateFromConnectionString(
StandardStorageConnectionString(), m_containerName, RandomString());
auto blobClientWithoutAuth = Azure::Storage::Blobs::BlockBlobClient(blobClient.GetUrl());
{
auto response = blobClient.DeleteIfExists();
EXPECT_FALSE(response.HasValue());
}
std::vector<uint8_t> emptyContent;
blobClient.UploadFrom(emptyContent.data(), emptyContent.size());
EXPECT_THROW(blobClientWithoutAuth.DeleteIfExists(), StorageException);
{
auto response = blobClient.DeleteIfExists();
EXPECT_TRUE(response.HasValue());
}
blobClient.UploadFrom(emptyContent.data(), emptyContent.size());
auto snapshot = blobClient.CreateSnapshot()->Snapshot;
auto blobClientWithSnapshot = blobClient.WithSnapshot(snapshot);
{
auto response = blobClientWithSnapshot.DeleteIfExists();
EXPECT_TRUE(response.HasValue());
}
{
auto response = blobClientWithSnapshot.DeleteIfExists();
EXPECT_FALSE(response.HasValue());
}
}
}}} // namespace Azure::Storage::Test

View File

@ -251,4 +251,28 @@ namespace Azure { namespace Storage { namespace Test {
EXPECT_THROW(pageBlobClient.UploadPages(0, &pageContent, options), StorageException);
}
TEST_F(PageBlobClientTest, CreateIfNotExists)
{
auto blobClient = Azure::Storage::Blobs::PageBlobClient::CreateFromConnectionString(
StandardStorageConnectionString(), m_containerName, RandomString());
auto blobClientWithoutAuth = Azure::Storage::Blobs::PageBlobClient(blobClient.GetUrl());
EXPECT_THROW(blobClientWithoutAuth.CreateIfNotExists(m_blobContent.size()), StorageException);
{
auto response = blobClient.CreateIfNotExists(m_blobContent.size());
EXPECT_TRUE(response.HasValue());
}
auto blobContent
= Azure::Core::Http::MemoryBodyStream(m_blobContent.data(), m_blobContent.size());
blobClient.UploadPages(0, &blobContent);
{
auto response = blobClient.CreateIfNotExists(m_blobContent.size());
EXPECT_FALSE(response.HasValue());
}
auto downloadStream = std::move(blobClient.Download()->BodyStream);
EXPECT_EQ(
Azure::Core::Http::BodyStream::ReadToEnd(Azure::Core::Context(), *downloadStream),
m_blobContent);
}
}}} // namespace Azure::Storage::Test