Remove blob batch (#1309)

This commit is contained in:
JinmingHu 2021-01-11 15:13:49 +08:00 committed by GitHub
parent 967ccae1ff
commit 4527798bd1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 1 additions and 758 deletions

View File

@ -65,6 +65,7 @@
- Continuation token of result types are changed to nullable.
- Rename `Models::DeleteSnapshotsOption::Only` to `Models::DeleteSnapshotsOption::OnlySnapshots`.
- Rename `SourceConditions` in API options to `SourceAccessConditions`.
- Remove Blob Batch.
## 12.0.0-beta.5 (2020-11-13)

View File

@ -30,7 +30,6 @@ set(
AZURE_STORAGE_BLOB_HEADER
inc/azure/storage/blobs/protocol/blob_rest_client.hpp
inc/azure/storage/blobs/append_blob_client.hpp
inc/azure/storage/blobs/blob_batch_client.hpp
inc/azure/storage/blobs/blob_client.hpp
inc/azure/storage/blobs/blob_container_client.hpp
inc/azure/storage/blobs/blob_options.hpp
@ -46,7 +45,6 @@ set(
set(
AZURE_STORAGE_BLOB_SOURCE
src/append_blob_client.cpp
src/blob_batch_client.cpp
src/blob_client.cpp
src/blob_container_client.cpp
src/blob_rest_client.cpp
@ -84,7 +82,6 @@ if(BUILD_TESTING)
PRIVATE
test/append_blob_client_test.cpp
test/append_blob_client_test.hpp
test/blob_batch_client_test.cpp
test/blob_container_client_test.cpp
test/blob_container_client_test.hpp
test/blob_sas_test.cpp

View File

@ -4,7 +4,6 @@
#pragma once
#include "azure/storage/blobs/append_blob_client.hpp"
#include "azure/storage/blobs/blob_batch_client.hpp"
#include "azure/storage/blobs/blob_client.hpp"
#include "azure/storage/blobs/blob_container_client.hpp"
#include "azure/storage/blobs/blob_sas_builder.hpp"

View File

@ -1,170 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#pragma once
#include <cstdint>
#include <memory>
#include <string>
#include <vector>
#include "azure/storage/blobs/blob_service_client.hpp"
namespace Azure { namespace Storage { namespace Blobs {
/**
* @brief A BlobBatch allows you to batch multiple Azure Storage operations in a single request
* via BlobBatchClient.SubmitBatch.
*/
class BlobBatch {
public:
/**
* @brief Marks the specified blob or snapshot for deletion.
*
* @param blobContainerName The name of the container containing the blob to delete.
* @param blobName
* The name of the blob to delete.
* @param options Optional parameters to execute this
* function.
* @return An index of this operation result in
* SubmitBlobBatchResult.DeleteBlobResults, after this batch is submitted via
* BlobBatchClient.SubmitBatch.
*/
int32_t DeleteBlob(
const std::string& blobContainerName,
const std::string& blobName,
const DeleteBlobOptions& options = DeleteBlobOptions());
/**
* @brief Sets the tier on a blob.
*
* @param blobContainerName The name of the
* container containing the blob to set the tier of.
* @param blobName The name of the blob
* to set the tier of.
* @param tier Indicates the tier to be set on the blob.
*
* @param options Optional parameters to execute this function.
* @return An index of this
* operation result in SubmitBlobBatchResult.SetBlobAccessTierResults, after this batch is
* submitted via BlobBatchClient.SubmitBatch.
*/
int32_t SetBlobAccessTier(
const std::string& blobContainerName,
const std::string& blobName,
Models::AccessTier tier,
const SetBlobAccessTierOptions& options = SetBlobAccessTierOptions());
private:
friend class BlobBatchClient;
struct DeleteBlobSubRequest
{
std::string BlobContainerName;
std::string BlobName;
DeleteBlobOptions Options;
};
struct SetBlobAccessTierSubRequest
{
std::string BlobContainerName;
std::string BlobName;
Models::AccessTier Tier;
SetBlobAccessTierOptions Options;
};
std::vector<DeleteBlobSubRequest> m_deleteBlobSubRequests;
std::vector<SetBlobAccessTierSubRequest> m_setBlobAccessTierSubRequests;
};
struct SubmitBlobBatchResult
{
std::vector<Azure::Core::Response<Models::DeleteBlobResult>> DeleteBlobResults;
std::vector<Azure::Core::Response<Models::SetBlobAccessTierResult>> SetBlobAccessTierResults;
};
/**
* @brief The BlobBatchClient allows you to batch multiple Azure Storage operations in a
* single request.
*/
class BlobBatchClient {
public:
/**
* @brief Initialize a new instance of BlobBatchClient.
*
* @param connectionString A connection string includes the authentication information required
* for your application to access data in an Azure Storage account at runtime.
* @param options Optional client options that define the transport pipeline policies for
* authentication, retries, etc., that are applied to every request and subrequest.
* @return A new BlobBatchClient instance.
*/
static BlobBatchClient CreateFromConnectionString(
const std::string& connectionString,
const BlobClientOptions& options = BlobClientOptions());
/**
* @brief Initialize a new instance of BlobBatchClient.
*
* @param serviceUrl A url referencing the blob that includes the name of the account.
* @param credential The shared key credential used to sign requests.
* @param options Optional client options that define the transport pipeline policies for
* authentication, retries, etc., that are applied to every request and subrequest.
*/
explicit BlobBatchClient(
const std::string& serviceUrl,
std::shared_ptr<StorageSharedKeyCredential> credential,
const BlobClientOptions& options = BlobClientOptions());
/**
* @brief Initialize a new instance of BlobBatchClient.
*
* @param serviceUrl A url referencing the blob that includes the name of the account.
* @param credential The token credential used to sign requests.
* @param options Optional client options that define the transport pipeline policies for
* authentication, retries, etc., that are applied to every request and subrequest.
*/
explicit BlobBatchClient(
const std::string& serviceUrl,
std::shared_ptr<Core::TokenCredential> credential,
const BlobClientOptions& options = BlobClientOptions());
/**
* @brief Initialize a new instance of BlobBatchClient.
*
* @param serviceUrl A url referencing the blob that includes the name of the account, and
* possibly also a SAS token.
* @param options Optional client options that define the transport pipeline policies for
* authentication, retries, etc., that are applied to every request and subrequest.
*/
explicit BlobBatchClient(
const std::string& serviceUrl,
const BlobClientOptions& options = BlobClientOptions());
/**
* @brief Creates a new BlobBatch to collect sub-operations that can be submitted
* together via SubmitBatch.
*
* @return A new instance of BlobBatch.
*/
static BlobBatch CreateBatch() { return BlobBatch(); }
/**
* @brief Submit a BlobBatch of sub-operations.
*
* @param batch A BlobBatch
* of sub-operations.
* @param options Optional parameters to execute this function.
*
* @return A SubmitBlobBatchResult on successful submitting.
*/
Azure::Core::Response<SubmitBlobBatchResult> SubmitBatch(
const BlobBatch& batch,
const SubmitBlobBatchOptions& options = SubmitBlobBatchOptions()) const;
protected:
Azure::Core::Http::Url m_serviceUrl;
std::shared_ptr<Azure::Core::Http::HttpPipeline> m_pipeline;
std::shared_ptr<Azure::Core::Http::HttpPipeline> m_subRequestPipeline;
};
}}} // namespace Azure::Storage::Blobs

View File

@ -1,432 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#include "azure/storage/blobs/blob_batch_client.hpp"
#include <algorithm>
#include <cstring>
#include <azure/core/http/policy.hpp>
#include <azure/storage/common/constants.hpp>
#include <azure/storage/common/shared_key_policy.hpp>
#include <azure/storage/common/storage_per_retry_policy.hpp>
#include "azure/storage/blobs/version.hpp"
namespace Azure { namespace Storage { namespace Blobs {
namespace {
class NoopTransportPolicy : public Core::Http::HttpPolicy {
public:
~NoopTransportPolicy() override {}
std::unique_ptr<HttpPolicy> Clone() const override
{
return std::make_unique<NoopTransportPolicy>(*this);
}
std::unique_ptr<Core::Http::RawResponse> Send(
Core::Context const& context,
Core::Http::Request& request,
Core::Http::NextHttpPolicy nextHttpPolicy) const override
{
unused(context, request, nextHttpPolicy);
return std::unique_ptr<Core::Http::RawResponse>();
}
};
} // namespace
int32_t BlobBatch::DeleteBlob(
const std::string& blobContainerName,
const std::string& blobName,
const DeleteBlobOptions& options)
{
DeleteBlobSubRequest operation;
operation.BlobContainerName = blobContainerName;
operation.BlobName = blobName;
operation.Options = options;
m_deleteBlobSubRequests.emplace_back(std::move(operation));
return static_cast<int32_t>(m_deleteBlobSubRequests.size() - 1);
}
int32_t BlobBatch::SetBlobAccessTier(
const std::string& blobContainerName,
const std::string& blobName,
Models::AccessTier tier,
const SetBlobAccessTierOptions& options)
{
SetBlobAccessTierSubRequest operation;
operation.BlobContainerName = blobContainerName;
operation.BlobName = blobName;
operation.Options = options;
operation.Tier = tier;
m_setBlobAccessTierSubRequests.emplace_back(std::move(operation));
return static_cast<int32_t>(m_setBlobAccessTierSubRequests.size() - 1);
}
BlobBatchClient BlobBatchClient::CreateFromConnectionString(
const std::string& connectionString,
const BlobClientOptions& options)
{
auto parsedConnectionString = Storage::Details::ParseConnectionString(connectionString);
auto serviceUrl = std::move(parsedConnectionString.BlobServiceUrl);
if (parsedConnectionString.KeyCredential)
{
return BlobBatchClient(
serviceUrl.GetAbsoluteUrl(), parsedConnectionString.KeyCredential, options);
}
else
{
return BlobBatchClient(serviceUrl.GetAbsoluteUrl(), options);
}
}
BlobBatchClient::BlobBatchClient(
const std::string& serviceUrl,
std::shared_ptr<StorageSharedKeyCredential> credential,
const BlobClientOptions& options)
: m_serviceUrl(serviceUrl)
{
std::vector<std::unique_ptr<Azure::Core::Http::HttpPolicy>> policies;
policies.emplace_back(std::make_unique<Azure::Core::Http::TelemetryPolicy>(
Storage::Details::BlobServicePackageName, Details::Version::VersionString()));
policies.emplace_back(std::make_unique<Azure::Core::Http::RequestIdPolicy>());
for (const auto& p : options.PerOperationPolicies)
{
policies.emplace_back(p->Clone());
}
policies.emplace_back(
std::make_unique<Storage::Details::StorageRetryPolicy>(options.RetryOptions));
for (const auto& p : options.PerRetryPolicies)
{
policies.emplace_back(p->Clone());
}
policies.emplace_back(std::make_unique<Storage::Details::StoragePerRetryPolicy>());
policies.emplace_back(std::make_unique<Storage::Details::SharedKeyPolicy>(credential));
policies.emplace_back(
std::make_unique<Azure::Core::Http::TransportPolicy>(options.TransportPolicyOptions));
m_pipeline = std::make_shared<Azure::Core::Http::HttpPipeline>(policies);
policies.clear();
for (const auto& p : options.PerOperationPolicies)
{
policies.emplace_back(p->Clone());
}
for (const auto& p : options.PerRetryPolicies)
{
policies.emplace_back(p->Clone());
}
policies.emplace_back(std::make_unique<Storage::Details::StoragePerRetryPolicy>());
policies.emplace_back(std::make_unique<Storage::Details::SharedKeyPolicy>(credential));
policies.emplace_back(std::make_unique<NoopTransportPolicy>());
m_subRequestPipeline = std::make_shared<Azure::Core::Http::HttpPipeline>(policies);
}
BlobBatchClient::BlobBatchClient(
const std::string& serviceUrl,
std::shared_ptr<Core::TokenCredential> credential,
const BlobClientOptions& options)
: m_serviceUrl(serviceUrl)
{
std::vector<std::unique_ptr<Azure::Core::Http::HttpPolicy>> policies;
policies.emplace_back(std::make_unique<Azure::Core::Http::TelemetryPolicy>(
Storage::Details::BlobServicePackageName, Details::Version::VersionString()));
policies.emplace_back(std::make_unique<Azure::Core::Http::RequestIdPolicy>());
for (const auto& p : options.PerOperationPolicies)
{
policies.emplace_back(p->Clone());
}
policies.emplace_back(
std::make_unique<Storage::Details::StorageRetryPolicy>(options.RetryOptions));
for (const auto& p : options.PerRetryPolicies)
{
policies.emplace_back(p->Clone());
}
policies.emplace_back(std::make_unique<Storage::Details::StoragePerRetryPolicy>());
policies.emplace_back(std::make_unique<Core::Http::BearerTokenAuthenticationPolicy>(
credential, Storage::Details::StorageScope));
policies.emplace_back(
std::make_unique<Azure::Core::Http::TransportPolicy>(options.TransportPolicyOptions));
m_pipeline = std::make_shared<Azure::Core::Http::HttpPipeline>(policies);
policies.clear();
for (const auto& p : options.PerOperationPolicies)
{
policies.emplace_back(p->Clone());
}
for (const auto& p : options.PerRetryPolicies)
{
policies.emplace_back(p->Clone());
}
policies.emplace_back(std::make_unique<Storage::Details::StoragePerRetryPolicy>());
policies.emplace_back(std::make_unique<Core::Http::BearerTokenAuthenticationPolicy>(
credential, Storage::Details::StorageScope));
policies.emplace_back(std::make_unique<NoopTransportPolicy>());
m_subRequestPipeline = std::make_shared<Azure::Core::Http::HttpPipeline>(policies);
}
BlobBatchClient::BlobBatchClient(const std::string& serviceUrl, const BlobClientOptions& options)
: m_serviceUrl(serviceUrl)
{
std::vector<std::unique_ptr<Azure::Core::Http::HttpPolicy>> policies;
policies.emplace_back(std::make_unique<Azure::Core::Http::TelemetryPolicy>(
Storage::Details::BlobServicePackageName, Details::Version::VersionString()));
policies.emplace_back(std::make_unique<Azure::Core::Http::RequestIdPolicy>());
for (const auto& p : options.PerOperationPolicies)
{
policies.emplace_back(p->Clone());
}
policies.emplace_back(
std::make_unique<Storage::Details::StorageRetryPolicy>(options.RetryOptions));
for (const auto& p : options.PerRetryPolicies)
{
policies.emplace_back(p->Clone());
}
policies.emplace_back(std::make_unique<Storage::Details::StoragePerRetryPolicy>());
policies.emplace_back(
std::make_unique<Azure::Core::Http::TransportPolicy>(options.TransportPolicyOptions));
m_pipeline = std::make_shared<Azure::Core::Http::HttpPipeline>(policies);
policies.clear();
for (const auto& p : options.PerOperationPolicies)
{
policies.emplace_back(p->Clone());
}
for (const auto& p : options.PerRetryPolicies)
{
policies.emplace_back(p->Clone());
}
policies.emplace_back(std::make_unique<Storage::Details::StoragePerRetryPolicy>());
policies.emplace_back(std::make_unique<NoopTransportPolicy>());
m_subRequestPipeline = std::make_shared<Azure::Core::Http::HttpPipeline>(policies);
}
Azure::Core::Response<SubmitBlobBatchResult> BlobBatchClient::SubmitBatch(
const BlobBatch& batch,
const SubmitBlobBatchOptions& options) const
{
const std::string LineEnding = "\r\n";
const std::string ContentTypePrefix = "multipart/mixed; boundary=";
std::string boundary = "batch_" + Azure::Core::Uuid::CreateUuid().GetUuidString();
enum class RequestType
{
DeleteBlob,
SetBlobAccessTier,
};
std::vector<RequestType> requestTypes;
std::string requestBody;
{
auto getBatchBoundary = [&LineEnding, &boundary, subRequestCounter = 0]() mutable {
std::string ret;
ret += "--" + boundary + LineEnding;
ret += "Content-Type: application/http" + LineEnding + "Content-Transfer-Encoding: binary"
+ LineEnding + "Content-ID: " + std::to_string(subRequestCounter++) + LineEnding
+ LineEnding;
return ret;
};
for (const auto& subrequest : batch.m_deleteBlobSubRequests)
{
requestTypes.emplace_back(RequestType::DeleteBlob);
requestBody += getBatchBoundary();
auto blobUrl = m_serviceUrl;
blobUrl.AppendPath(Storage::Details::UrlEncodePath(subrequest.BlobContainerName));
blobUrl.AppendPath(Storage::Details::UrlEncodePath(subrequest.BlobName));
Details::BlobRestClient::Blob::DeleteBlobOptions protocolLayerOptions;
protocolLayerOptions.DeleteSnapshots = subrequest.Options.DeleteSnapshots;
protocolLayerOptions.IfModifiedSince = subrequest.Options.AccessConditions.IfModifiedSince;
protocolLayerOptions.IfUnmodifiedSince
= subrequest.Options.AccessConditions.IfUnmodifiedSince;
protocolLayerOptions.IfMatch = subrequest.Options.AccessConditions.IfMatch;
protocolLayerOptions.IfNoneMatch = subrequest.Options.AccessConditions.IfNoneMatch;
protocolLayerOptions.LeaseId = subrequest.Options.AccessConditions.LeaseId;
auto message
= Details::BlobRestClient::Blob::DeleteCreateMessage(blobUrl, protocolLayerOptions);
message.RemoveHeader(Storage::Details::HttpHeaderXMsVersion);
m_subRequestPipeline->Send(options.Context, message);
requestBody += message.GetHTTPMessagePreBody();
}
for (const auto& subrequest : batch.m_setBlobAccessTierSubRequests)
{
requestTypes.emplace_back(RequestType::SetBlobAccessTier);
requestBody += getBatchBoundary();
auto blobUrl = m_serviceUrl;
blobUrl.AppendPath(Storage::Details::UrlEncodePath(subrequest.BlobContainerName));
blobUrl.AppendPath(Storage::Details::UrlEncodePath(subrequest.BlobName));
Details::BlobRestClient::Blob::SetBlobAccessTierOptions protocolLayerOptions;
protocolLayerOptions.Tier = subrequest.Tier;
protocolLayerOptions.RehydratePriority = subrequest.Options.RehydratePriority;
auto message = Details::BlobRestClient::Blob::SetAccessTierCreateMessage(
blobUrl, protocolLayerOptions);
message.RemoveHeader(Storage::Details::HttpHeaderXMsVersion);
m_subRequestPipeline->Send(options.Context, message);
requestBody += message.GetHTTPMessagePreBody();
}
requestBody += "--" + boundary + "--" + LineEnding;
}
Details::BlobRestClient::BlobBatch::SubmitBlobBatchOptions protocolLayerOptions;
protocolLayerOptions.ContentType = ContentTypePrefix + boundary;
Azure::Core::Http::MemoryBodyStream requestBodyStream(
reinterpret_cast<const uint8_t*>(requestBody.data()), requestBody.length());
auto rawResponse = Details::BlobRestClient::BlobBatch::SubmitBatch(
options.Context, *m_pipeline, m_serviceUrl, &requestBodyStream, protocolLayerOptions);
if (rawResponse->ContentType.substr(0, ContentTypePrefix.length()) == ContentTypePrefix)
{
boundary = rawResponse->ContentType.substr(ContentTypePrefix.length());
}
else
{
throw std::runtime_error("failed to parse Content-Type response header");
}
SubmitBlobBatchResult batchResult;
{
const std::vector<uint8_t>& responseBody = rawResponse.GetRawResponse().GetBody();
const char* const startPos = reinterpret_cast<const char*>(responseBody.data());
const char* currPos = startPos;
const char* const endPos = currPos + responseBody.size();
auto parseLookAhead = [&currPos, endPos](const std::string& expect) -> bool {
// This doesn't move currPos
for (std::size_t i = 0; i < expect.length(); ++i)
{
if (currPos + i < endPos && currPos[i] == expect[i])
{
continue;
}
return false;
}
return true;
};
auto parseConsume = [&currPos, startPos, &parseLookAhead](const std::string& expect) -> void {
// This moves currPos
if (parseLookAhead(expect))
{
currPos += expect.length();
}
else
{
throw std::runtime_error(
"failed to parse response body at " + std::to_string(currPos - startPos));
}
};
auto parseFindNext = [&currPos, endPos](const std::string& expect) -> const char* {
// This doesn't move currPos
return std::search(currPos, endPos, expect.begin(), expect.end());
};
auto parseFindNextAfter = [endPos, &parseFindNext](const std::string& expect) -> const char* {
// This doesn't move currPos
return std::min(endPos, parseFindNext(expect) + expect.length());
};
auto parseGetUntilAfter
= [&currPos, endPos, &parseFindNext](const std::string& expect) -> std::string {
// This moves currPos
auto ePos = parseFindNext(expect);
std::string ret(currPos, ePos);
currPos = std::min(endPos, ePos + expect.length());
return ret;
};
int subRequestCounter = 0;
while (true)
{
parseConsume("--" + boundary);
if (parseLookAhead("--"))
{
parseConsume("--");
}
if (currPos == endPos)
{
break;
}
currPos = parseFindNextAfter(LineEnding + LineEnding);
auto boundaryPos = parseFindNext("--" + boundary);
// now (currPos, boundaryPos) is a subresponse body
parseConsume("HTTP/");
int32_t httpMajorVersion = std::stoi(parseGetUntilAfter("."));
int32_t httpMinorVersion = std::stoi(parseGetUntilAfter(" "));
int32_t httpStatusCode = std::stoi(parseGetUntilAfter(" "));
std::string httpReasonPhrase = parseGetUntilAfter(LineEnding);
auto rawSubresponse = std::make_unique<Azure::Core::Http::RawResponse>(
httpMajorVersion,
httpMinorVersion,
static_cast<Azure::Core::Http::HttpStatusCode>(httpStatusCode),
httpReasonPhrase);
while (currPos < boundaryPos)
{
if (parseLookAhead(LineEnding))
{
break;
}
std::string headerName = parseGetUntilAfter(": ");
std::string headerValue = parseGetUntilAfter(LineEnding);
rawSubresponse->AddHeader(headerName, headerValue);
}
parseConsume(LineEnding);
rawSubresponse->SetBody(std::vector<uint8_t>(currPos, boundaryPos));
currPos = boundaryPos;
RequestType requestType = requestTypes[subRequestCounter++];
if (requestType == RequestType::DeleteBlob)
{
try
{
batchResult.DeleteBlobResults.emplace_back(
Details::BlobRestClient::Blob::DeleteCreateResponse(
options.Context, std::move(rawSubresponse)));
}
catch (StorageException& e)
{
batchResult.DeleteBlobResults.emplace_back(
Azure::Core::Response<Models::DeleteBlobResult>(
Models::DeleteBlobResult{}, std::move(e.RawResponse)));
}
}
else if (requestType == RequestType::SetBlobAccessTier)
{
try
{
batchResult.SetBlobAccessTierResults.emplace_back(
Details::BlobRestClient::Blob::SetAccessTierCreateResponse(
options.Context, std::move(rawSubresponse)));
}
catch (StorageException& e)
{
batchResult.SetBlobAccessTierResults.emplace_back(
Azure::Core::Response<Models::SetBlobAccessTierResult>(
Models::SetBlobAccessTierResult{}, std::move(e.RawResponse)));
}
}
}
}
return Azure::Core::Response<SubmitBlobBatchResult>(
std::move(batchResult), rawResponse.ExtractRawResponse());
}
}}} // namespace Azure::Storage::Blobs

View File

@ -1,152 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#include <thread>
#include <azure/storage/blobs.hpp>
#include <azure/storage/blobs/blob_sas_builder.hpp>
#include "test_base.hpp"
namespace Azure { namespace Storage { namespace Test {
class BlobBatchClientTest : public ::testing::Test {
protected:
BlobBatchClientTest()
: m_blobBatchClient(Azure::Storage::Blobs::BlobBatchClient::CreateFromConnectionString(
StandardStorageConnectionString()))
{
}
Azure::Storage::Blobs::BlobBatchClient m_blobBatchClient;
};
TEST_F(BlobBatchClientTest, BatchSasAuth)
{
Sas::AccountSasBuilder accountSasBuilder;
accountSasBuilder.Protocol = Sas::SasProtocol::HttpsAndHttp;
accountSasBuilder.StartsOn = Azure::Core::DateTime::Now() - std::chrono::minutes(5);
accountSasBuilder.ExpiresOn = Azure::Core::DateTime::Now() + std::chrono::minutes(60);
accountSasBuilder.Services = Sas::AccountSasServices::Blobs;
accountSasBuilder.ResourceTypes
= Sas::AccountSasResource::Object | Sas::AccountSasResource::BlobContainer;
accountSasBuilder.SetPermissions(Sas::AccountSasPermissions::All);
auto keyCredential
= Details::ParseConnectionString(StandardStorageConnectionString()).KeyCredential;
auto serviceClient
= Blobs::BlobServiceClient::CreateFromConnectionString(StandardStorageConnectionString());
std::string containerName = LowercaseRandomString();
auto containerClient = serviceClient.GetBlobContainerClient(containerName);
containerClient.Create();
std::string blobName = RandomString();
auto blobClient = containerClient.GetBlockBlobClient(blobName);
blobClient.UploadFrom(nullptr, 0);
auto batch = Azure::Storage::Blobs::BlobBatchClient::CreateBatch();
batch.DeleteBlob(containerName, blobName);
auto batchClient = Blobs::BlobBatchClient(serviceClient.GetUrl());
EXPECT_THROW(batchClient.SubmitBatch(batch), StorageException);
batchClient = Blobs::BlobBatchClient(
serviceClient.GetUrl() + accountSasBuilder.GenerateSasToken(*keyCredential));
EXPECT_NO_THROW(batchClient.SubmitBatch(batch));
containerClient.Delete();
}
TEST_F(BlobBatchClientTest, Batch)
{
auto serviceClient
= Blobs::BlobServiceClient::CreateFromConnectionString(StandardStorageConnectionString());
std::string containerName1 = LowercaseRandomString();
std::string containerName2 = LowercaseRandomString();
auto containerClient1 = serviceClient.GetBlobContainerClient(containerName1);
containerClient1.Create();
auto containerClient2 = serviceClient.GetBlobContainerClient(containerName2);
containerClient2.Create();
std::string blobName11 = RandomString();
auto blobClient11 = containerClient1.GetBlockBlobClient(blobName11);
blobClient11.UploadFrom(nullptr, 0);
std::string blobName12 = RandomString();
auto blobClient12 = containerClient1.GetBlockBlobClient(blobName12);
blobClient12.UploadFrom(nullptr, 0);
std::string blobName21 = RandomString();
auto blobClient21 = containerClient2.GetBlockBlobClient(blobName21);
blobClient21.UploadFrom(nullptr, 0);
std::string blobName22 = RandomString();
auto batch = Azure::Storage::Blobs::BlobBatchClient::CreateBatch();
int32_t id1
= batch.SetBlobAccessTier(containerName1, blobName11, Blobs::Models::AccessTier::Cool);
int32_t id2
= batch.SetBlobAccessTier(containerName1, blobName12, Blobs::Models::AccessTier::Hot);
int32_t id3
= batch.SetBlobAccessTier(containerName2, blobName21, Blobs::Models::AccessTier::Hot);
int32_t id4
= batch.SetBlobAccessTier(containerName2, blobName22, Blobs::Models::AccessTier::Cool);
unused(id1, id2, id3, id4);
std::size_t failedId = static_cast<std::size_t>(id4);
std::size_t batchSize = static_cast<std::size_t>(id4) + 1;
auto batchResult = m_blobBatchClient.SubmitBatch(batch);
EXPECT_EQ(batchResult->SetBlobAccessTierResults.size(), batchSize);
EXPECT_TRUE(batchResult->DeleteBlobResults.empty());
for (std::size_t i = 0; i < batchSize; ++i)
{
if (i != failedId)
{
EXPECT_EQ(
batchResult->SetBlobAccessTierResults[i].GetRawResponse().GetStatusCode(),
Azure::Core::Http::HttpStatusCode::Ok);
}
else
{
EXPECT_NE(
batchResult->SetBlobAccessTierResults[i].GetRawResponse().GetStatusCode(),
Azure::Core::Http::HttpStatusCode::Ok);
}
}
batch = Azure::Storage::Blobs::BlobBatchClient::CreateBatch();
id1 = batch.DeleteBlob(containerName1, blobName11);
id2 = batch.DeleteBlob(containerName1, blobName12);
id3 = batch.DeleteBlob(containerName2, blobName21);
id4 = batch.DeleteBlob(containerName2, blobName22);
failedId = static_cast<std::size_t>(id4);
batchSize = static_cast<std::size_t>(id4) + 1;
batchResult = m_blobBatchClient.SubmitBatch(batch);
EXPECT_EQ(batchResult->DeleteBlobResults.size(), batchSize);
EXPECT_TRUE(batchResult->SetBlobAccessTierResults.empty());
for (std::size_t i = 0; i < batchSize; ++i)
{
if (i != failedId)
{
EXPECT_EQ(
batchResult->DeleteBlobResults[i].GetRawResponse().GetStatusCode(),
Azure::Core::Http::HttpStatusCode::Accepted);
}
else
{
EXPECT_NE(
batchResult->DeleteBlobResults[i].GetRawResponse().GetStatusCode(),
Azure::Core::Http::HttpStatusCode::Accepted);
}
}
containerClient1.Delete();
containerClient2.Delete();
}
}}} // namespace Azure::Storage::Test