Support poll operations in blob service (#1461)

* operation<T>

* add test cases

* changelog

* fix build error

* add virtual destructor

* fix build error

* use Azure::Core::RequestFailedException
This commit is contained in:
JinmingHu 2021-01-26 16:00:20 +08:00 committed by GitHub
parent a850513e28
commit ae53a38c7a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 225 additions and 85 deletions

View File

@ -34,6 +34,8 @@ namespace Azure { namespace Core {
OperationStatus m_status = OperationStatus::NotStarted;
public:
virtual ~Operation() {}
/**
* @brief Final reuslt of the long-running operation.
*

View File

@ -15,6 +15,7 @@
- Type for lease duration in requests was changed to `std::chrono::seconds`, in response was changed to enum.
- `PublicAccessType::Private` was renamed to `PublicAccessType::None`.
- `startsOn` parameter for `GetUserDelegationKey` was changed to optional.
- Return types of `BlobClient::StartCopyFromUri` and `PageBlobClient::StartCopyIncremental` were changed to `StartCopyBlobResult`, supporting poll operations.
## 12.0.0-beta.6 (2020-01-14)

View File

@ -50,6 +50,7 @@ set(
src/blob_client.cpp
src/blob_container_client.cpp
src/blob_lease_client.cpp
src/blob_responses.cpp
src/blob_rest_client.cpp
src/blob_sas_builder.cpp
src/blob_service_client.cpp

View File

@ -199,9 +199,9 @@ namespace Azure { namespace Storage { namespace Blobs {
* public or must be authenticated via a shared access signature. If the source blob is public,
* no authentication is required to perform the copy operation.
* @param options Optional parameters to execute this function.
* @return A StartCopyBlobFromUriResult describing the state of the copy operation.
* @return A StartCopyBlobResult describing the state of the copy operation.
*/
Azure::Core::Response<Models::StartCopyBlobFromUriResult> StartCopyFromUri(
Azure::Core::Response<Models::StartCopyBlobResult> StartCopyFromUri(
const std::string& sourceUri,
const StartCopyBlobFromUriOptions& options = StartCopyBlobFromUriOptions()) const;

View File

@ -5,63 +5,107 @@
#include <cstdint>
#include <string>
#include <thread>
#include <vector>
#include <azure/core/operation.hpp>
#include "azure/storage/blobs/protocol/blob_rest_client.hpp"
namespace Azure { namespace Storage { namespace Blobs { namespace Models {
namespace Azure { namespace Storage { namespace Blobs {
struct DownloadBlobToResult
{
std::string ETag;
Azure::Core::DateTime LastModified;
int64_t ContentLength = 0;
BlobHttpHeaders HttpHeaders;
Storage::Metadata Metadata;
Models::BlobType BlobType;
bool IsServerEncrypted = false;
Azure::Core::Nullable<std::vector<uint8_t>> EncryptionKeySha256;
};
class BlobClient;
class PageBlobClient;
using UploadBlockBlobFromResult = UploadBlockBlobResult;
namespace Models {
struct AcquireBlobLeaseResult
{
std::string RequestId;
std::string ETag;
Azure::Core::DateTime LastModified;
std::string LeaseId;
};
struct DownloadBlobToResult
{
std::string ETag;
Azure::Core::DateTime LastModified;
int64_t ContentLength = 0;
BlobHttpHeaders HttpHeaders;
Storage::Metadata Metadata;
Models::BlobType BlobType;
bool IsServerEncrypted = false;
Azure::Core::Nullable<std::vector<uint8_t>> EncryptionKeySha256;
};
struct BreakBlobLeaseResult
{
std::string RequestId;
std::string ETag;
Azure::Core::DateTime LastModified;
int32_t LeaseTime = 0;
};
using UploadBlockBlobFromResult = UploadBlockBlobResult;
struct ChangeBlobLeaseResult
{
std::string RequestId;
std::string ETag;
Azure::Core::DateTime LastModified;
std::string LeaseId;
};
struct AcquireBlobLeaseResult
{
std::string RequestId;
std::string ETag;
Azure::Core::DateTime LastModified;
std::string LeaseId;
};
struct ReleaseBlobLeaseResult
{
std::string RequestId;
std::string ETag;
Azure::Core::DateTime LastModified;
};
struct BreakBlobLeaseResult
{
std::string RequestId;
std::string ETag;
Azure::Core::DateTime LastModified;
int32_t LeaseTime = 0;
};
struct RenewBlobLeaseResult
{
std::string RequestId;
std::string ETag;
Azure::Core::DateTime LastModified;
std::string LeaseId;
};
struct ChangeBlobLeaseResult
{
std::string RequestId;
std::string ETag;
Azure::Core::DateTime LastModified;
std::string LeaseId;
};
}}}} // namespace Azure::Storage::Blobs::Models
struct ReleaseBlobLeaseResult
{
std::string RequestId;
std::string ETag;
Azure::Core::DateTime LastModified;
};
struct RenewBlobLeaseResult
{
std::string RequestId;
std::string ETag;
Azure::Core::DateTime LastModified;
std::string LeaseId;
};
class StartCopyBlobResult : public Azure::Core::Operation<GetBlobPropertiesResult> {
public:
std::string RequestId;
std::string ETag;
Azure::Core::DateTime LastModified;
std::string CopyId;
Models::CopyStatus CopyStatus;
Azure::Core::Nullable<std::string> VersionId;
public:
GetBlobPropertiesResult Value() const override { return m_pollResult; }
~StartCopyBlobResult() override {}
private:
std::string GetResumeToken() const override
{
// Not supported
std::abort();
}
std::unique_ptr<Azure::Core::Http::RawResponse> PollInternal(
Azure::Core::Context& context) override;
Azure::Core::Response<GetBlobPropertiesResult> PollUntilDoneInternal(
Azure::Core::Context& context,
std::chrono::milliseconds period) override;
std::shared_ptr<BlobClient> m_blobClient;
Models::GetBlobPropertiesResult m_pollResult;
friend class Blobs::BlobClient;
friend class Blobs::PageBlobClient;
};
} // namespace Models
}}} // namespace Azure::Storage::Blobs

View File

@ -248,9 +248,9 @@ namespace Azure { namespace Storage { namespace Blobs {
* @param sourceUri Specifies the to the source page blob as a uri up to 2 KB in length. The
* source blob must either be public or must be authenticated via a shared access signature.
* @param options Optional parameters to execute this function.
* @return A StartCopyPageBlobIncrementalResult describing the state of the copy operation.
* @return A StartCopyBlobResult describing the state of the copy operation.
*/
Azure::Core::Response<Models::StartCopyPageBlobIncrementalResult> StartCopyIncremental(
Azure::Core::Response<Models::StartCopyBlobResult> StartCopyIncremental(
const std::string& sourceUri,
const StartCopyPageBlobIncrementalOptions& options
= StartCopyPageBlobIncrementalOptions()) const;

View File

@ -844,25 +844,29 @@ namespace Azure { namespace Storage { namespace Blobs {
ObjectReplicationStatus ReplicationStatus;
}; // struct ObjectReplicationRule
struct StartCopyBlobFromUriResult
{
std::string RequestId;
std::string ETag;
Azure::Core::DateTime LastModified;
std::string CopyId;
Models::CopyStatus CopyStatus;
Azure::Core::Nullable<std::string> VersionId;
}; // struct StartCopyBlobFromUriResult
namespace Details {
struct StartCopyBlobFromUriResult
{
std::string RequestId;
std::string ETag;
Azure::Core::DateTime LastModified;
std::string CopyId;
Models::CopyStatus CopyStatus;
Azure::Core::Nullable<std::string> VersionId;
}; // struct StartCopyBlobFromUriResult
} // namespace Details
struct StartCopyPageBlobIncrementalResult
{
std::string RequestId;
std::string ETag;
Azure::Core::DateTime LastModified;
std::string CopyId;
Models::CopyStatus CopyStatus;
Azure::Core::Nullable<std::string> VersionId;
}; // struct StartCopyPageBlobIncrementalResult
namespace Details {
struct StartCopyPageBlobIncrementalResult
{
std::string RequestId;
std::string ETag;
Azure::Core::DateTime LastModified;
std::string CopyId;
Models::CopyStatus CopyStatus;
Azure::Core::Nullable<std::string> VersionId;
}; // struct StartCopyPageBlobIncrementalResult
} // namespace Details
struct AppendBlockFromUriResult
{
@ -5975,7 +5979,7 @@ namespace Azure { namespace Storage { namespace Blobs {
Azure::Core::Nullable<bool> ShouldSealDestination;
}; // struct StartCopyBlobFromUriOptions
static Azure::Core::Response<StartCopyBlobFromUriResult> StartCopyFromUri(
static Azure::Core::Response<Models::Details::StartCopyBlobFromUriResult> StartCopyFromUri(
const Azure::Core::Context& context,
Azure::Core::Http::HttpPipeline& pipeline,
const Azure::Core::Http::Url& url,
@ -6071,7 +6075,7 @@ namespace Azure { namespace Storage { namespace Blobs {
}
auto pHttpResponse = pipeline.Send(context, request);
Azure::Core::Http::RawResponse& httpResponse = *pHttpResponse;
StartCopyBlobFromUriResult response;
Models::Details::StartCopyBlobFromUriResult response;
auto http_status_code
= static_cast<std::underlying_type<Azure::Core::Http::HttpStatusCode>::type>(
httpResponse.GetStatusCode());
@ -6091,7 +6095,7 @@ namespace Azure { namespace Storage { namespace Blobs {
{
response.VersionId = x_ms_version_id__iterator->second;
}
return Azure::Core::Response<StartCopyBlobFromUriResult>(
return Azure::Core::Response<Models::Details::StartCopyBlobFromUriResult>(
std::move(response), std::move(pHttpResponse));
}
@ -8661,7 +8665,8 @@ namespace Azure { namespace Storage { namespace Blobs {
Azure::Core::Nullable<std::string> IfTags;
}; // struct StartCopyPageBlobIncrementalOptions
static Azure::Core::Response<StartCopyPageBlobIncrementalResult> StartCopyIncremental(
static Azure::Core::Response<Models::Details::StartCopyPageBlobIncrementalResult>
StartCopyIncremental(
const Azure::Core::Context& context,
Azure::Core::Http::HttpPipeline& pipeline,
const Azure::Core::Http::Url& url,
@ -8706,7 +8711,7 @@ namespace Azure { namespace Storage { namespace Blobs {
}
auto pHttpResponse = pipeline.Send(context, request);
Azure::Core::Http::RawResponse& httpResponse = *pHttpResponse;
StartCopyPageBlobIncrementalResult response;
Models::Details::StartCopyPageBlobIncrementalResult response;
auto http_status_code
= static_cast<std::underlying_type<Azure::Core::Http::HttpStatusCode>::type>(
httpResponse.GetStatusCode());
@ -8726,7 +8731,7 @@ namespace Azure { namespace Storage { namespace Blobs {
{
response.VersionId = x_ms_version_id__iterator->second;
}
return Azure::Core::Response<StartCopyPageBlobIncrementalResult>(
return Azure::Core::Response<Models::Details::StartCopyPageBlobIncrementalResult>(
std::move(response), std::move(pHttpResponse));
}

View File

@ -545,7 +545,7 @@ namespace Azure { namespace Storage { namespace Blobs {
options.Context, *m_pipeline, m_blobUrl, protocolLayerOptions);
}
Azure::Core::Response<Models::StartCopyBlobFromUriResult> BlobClient::StartCopyFromUri(
Azure::Core::Response<Models::StartCopyBlobResult> BlobClient::StartCopyFromUri(
const std::string& sourceUri,
const StartCopyBlobFromUriOptions& options) const
{
@ -567,8 +567,19 @@ namespace Azure { namespace Storage { namespace Blobs {
protocolLayerOptions.SourceIfNoneMatch = options.SourceAccessConditions.IfNoneMatch;
protocolLayerOptions.ShouldSealDestination = options.ShouldSealDestination;
protocolLayerOptions.SourceIfTags = options.SourceAccessConditions.TagConditions;
return Details::BlobRestClient::Blob::StartCopyFromUri(
auto response = Details::BlobRestClient::Blob::StartCopyFromUri(
options.Context, *m_pipeline, m_blobUrl, protocolLayerOptions);
Models::StartCopyBlobResult res;
res.RequestId = std::move(response->RequestId);
res.ETag = std::move(response->ETag);
res.LastModified = std::move(response->LastModified);
res.CopyId = std::move(response->CopyId);
res.CopyStatus = std::move(response->CopyStatus);
res.VersionId = std::move(response->VersionId);
res.m_blobClient = std::make_shared<BlobClient>(*this);
return Azure::Core::Response<Models::StartCopyBlobResult>(
std::move(res), response.ExtractRawResponse());
}
Azure::Core::Response<Models::AbortCopyBlobFromUriResult> BlobClient::AbortCopyFromUri(

View File

@ -0,0 +1,61 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#include "azure/storage/blobs/blob_responses.hpp"
#include "azure/storage/blobs/blob_client.hpp"
namespace Azure { namespace Storage { namespace Blobs { namespace Models {
std::unique_ptr<Azure::Core::Http::RawResponse> StartCopyBlobResult::PollInternal(
Azure::Core::Context& context)
{
unused(context);
auto response = m_blobClient->GetProperties();
if (!response->CopyStatus.HasValue())
{
m_status = Azure::Core::OperationStatus::Failed;
}
else if (response->CopyStatus.GetValue() == CopyStatus::Pending)
{
m_status = Azure::Core::OperationStatus::Running;
}
else if (response->CopyStatus.GetValue() == CopyStatus::Success)
{
m_status = Azure::Core::OperationStatus::Succeeded;
}
else
{
m_status = Azure::Core::OperationStatus::Failed;
}
m_pollResult = *response;
return response.ExtractRawResponse();
}
Azure::Core::Response<GetBlobPropertiesResult> StartCopyBlobResult::PollUntilDoneInternal(
Azure::Core::Context& context,
std::chrono::milliseconds period)
{
while (true)
{
auto rawResponse = PollInternal(context);
if (m_status == Azure::Core::OperationStatus::Succeeded)
{
return Azure::Core::Response<GetBlobPropertiesResult>(m_pollResult, std::move(rawResponse));
}
else if (m_status == Azure::Core::OperationStatus::Failed)
{
throw Azure::Core::RequestFailedException("Operation failed");
}
else if (m_status == Azure::Core::OperationStatus::Cancelled)
{
throw Azure::Core::RequestFailedException("Operation was cancelled");
}
std::this_thread::sleep_for(period);
};
}
}}}} // namespace Azure::Storage::Blobs::Models

View File

@ -279,8 +279,7 @@ namespace Azure { namespace Storage { namespace Blobs {
options.Context, *m_pipeline, m_blobUrl, protocolLayerOptions);
}
Azure::Core::Response<Models::StartCopyPageBlobIncrementalResult>
PageBlobClient::StartCopyIncremental(
Azure::Core::Response<Models::StartCopyBlobResult> PageBlobClient::StartCopyIncremental(
const std::string& sourceUri,
const StartCopyPageBlobIncrementalOptions& options) const
{
@ -291,8 +290,19 @@ namespace Azure { namespace Storage { namespace Blobs {
protocolLayerOptions.IfMatch = options.AccessConditions.IfMatch;
protocolLayerOptions.IfNoneMatch = options.AccessConditions.IfNoneMatch;
protocolLayerOptions.IfTags = options.AccessConditions.TagConditions;
return Details::BlobRestClient::PageBlob::StartCopyIncremental(
auto response = Details::BlobRestClient::PageBlob::StartCopyIncremental(
options.Context, *m_pipeline, m_blobUrl, protocolLayerOptions);
Models::StartCopyBlobResult res;
res.RequestId = std::move(response->RequestId);
res.ETag = std::move(response->ETag);
res.LastModified = std::move(response->LastModified);
res.CopyId = std::move(response->CopyId);
res.CopyStatus = std::move(response->CopyStatus);
res.VersionId = std::move(response->VersionId);
res.m_blobClient = std::make_shared<BlobClient>(*this);
return Azure::Core::Response<Models::StartCopyBlobResult>(
std::move(res), response.ExtractRawResponse());
}
}}} // namespace Azure::Storage::Blobs

View File

@ -327,8 +327,9 @@ namespace Azure { namespace Storage { namespace Test {
Blobs::StartCopyBlobFromUriOptions copyOptions;
copyOptions.ShouldSealDestination = false;
auto copyResult = blobClient2.StartCopyFromUri(blobClient.GetUrl() + GetSas(), copyOptions);
// TODO: poller wait here
getPropertiesResult = blobClient2.GetProperties();
getPropertiesResult = copyResult->PollUntilDone(std::chrono::seconds(1));
ASSERT_TRUE(getPropertiesResult->CopyStatus.HasValue());
EXPECT_EQ(getPropertiesResult->CopyStatus.GetValue(), Blobs::Models::CopyStatus::Success);
if (getPropertiesResult->IsSealed.HasValue())
{
EXPECT_FALSE(getPropertiesResult->IsSealed.GetValue());
@ -336,10 +337,11 @@ namespace Azure { namespace Storage { namespace Test {
copyOptions.ShouldSealDestination = true;
copyResult = blobClient2.StartCopyFromUri(blobClient.GetUrl() + GetSas(), copyOptions);
// TODO: poller wait here
getPropertiesResult = blobClient2.GetProperties();
getPropertiesResult = copyResult->PollUntilDone(std::chrono::seconds(1));
EXPECT_TRUE(getPropertiesResult->IsSealed.HasValue());
EXPECT_TRUE(getPropertiesResult->IsSealed.GetValue());
ASSERT_TRUE(getPropertiesResult->CopyStatus.HasValue());
EXPECT_EQ(getPropertiesResult->CopyStatus.GetValue(), Blobs::Models::CopyStatus::Success);
}
TEST_F(AppendBlobClientTest, CreateIfNotExists)

View File

@ -157,6 +157,9 @@ namespace Azure { namespace Storage { namespace Test {
EXPECT_FALSE(copyInfo->CopyStatus.Get().empty());
EXPECT_TRUE(copyInfo->VersionId.HasValue());
EXPECT_FALSE(copyInfo->VersionId.GetValue().empty());
auto getPropertiesResult = copyInfo->PollUntilDone(std::chrono::seconds(1));
ASSERT_TRUE(getPropertiesResult->CopyStatus.HasValue());
EXPECT_EQ(getPropertiesResult->CopyStatus.GetValue(), Blobs::Models::CopyStatus::Success);
}
TEST_F(PageBlobClientTest, Lease)