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:
parent
c3afb7798f
commit
9febf43832
@ -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.
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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.
|
||||
*
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
{
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user