New API: CopyFromUri, which copies a blob synchronously (#3098)

* sync copy blob

* CL

* Update sdk/storage/azure-storage-blobs/CHANGELOG.md

Co-authored-by: Ahson Khan <ahkha@microsoft.com>

* Update sdk/storage/azure-storage-blobs/inc/azure/storage/blobs/blob_client.hpp

Co-authored-by: Ahson Khan <ahkha@microsoft.com>

* Update sdk/storage/azure-storage-blobs/inc/azure/storage/blobs/blob_client.hpp

Co-authored-by: Ahson Khan <ahkha@microsoft.com>

Co-authored-by: Ahson Khan <ahkha@microsoft.com>
This commit is contained in:
JinmingHu 2021-11-17 10:07:40 +08:00 committed by GitHub
parent 1f07a30133
commit f9157c8763
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 497 additions and 50 deletions

View File

@ -1,5 +1,17 @@
# Release History
## 12.3.0 (Unreleased)
### Features Added
- New API: `BlobClient::CopyFromUri()`.
### Breaking Changes
### Bugs Fixed
### Other Changes
## 12.2.1 (2021-11-08)
### Other Changes

View File

@ -202,6 +202,23 @@ namespace Azure { namespace Storage { namespace Blobs {
const SetBlobAccessTierOptions& options = SetBlobAccessTierOptions(),
const Azure::Core::Context& context = Azure::Core::Context()) const;
/**
* @brief Copies data from the source to this blob, synchronously.
*
* @param sourceUri Specifies the URL of the source blob. The value may be a URL of up to 2 KB
* in length that specifies a blob. The value should be URL-encoded as it would appear in a
* request URI. The source blob must either be public or must be authorized via a shared access
* signature. If the size of the source blob is greater than 256 MB, the request will fail with
* 409 (Conflict). The blob type of the source blob has to be block blob.
* @param options Optional parameters to execute this function.
* @param context Context for cancelling long running operations.
* @return A CopyBlobFromUriResult describing the copy result.
*/
Azure::Response<Models::CopyBlobFromUriResult> CopyFromUri(
const std::string& sourceUri,
const CopyBlobFromUriOptions& options = CopyBlobFromUriOptions(),
const Azure::Core::Context& context = Azure::Core::Context()) const;
/**
* @brief Copies data at from the source to this blob.
*

View File

@ -497,6 +497,52 @@ namespace Azure { namespace Storage { namespace Blobs {
Azure::Nullable<bool> ShouldSealDestination;
};
/**
* @brief Optional parameters for #Azure::Storage::Blobs::BlobClient::CopyFromUri.
*/
struct CopyBlobFromUriOptions
{
/**
* @brief Specifies user-defined name-value pairs associated with the blob. If no
* name-value pairs are specified, the operation will copy the metadata from the source blob or
* file to the destination blob. If one or more name-value pairs are specified, the destination
* blob is created with the specified metadata, and metadata is not copied from the source blob
* or file.
*/
Storage::Metadata Metadata;
/**
* @brief The tags to set for this blob.
*/
std::map<std::string, std::string> Tags;
/**
* @brief Optional conditions that must be met to perform this operation.
*/
BlobAccessConditions AccessConditions;
/**
* @brief Optional conditions that the source must meet to perform this operation.
*
* @note Lease access condition only works for API versions before 2012-02-12.
*/
struct : public Azure::ModifiedConditions, public Azure::MatchConditions
{
} SourceAccessConditions;
/**
* @brief Specifies the tier to be set on the target blob.
*/
Azure::Nullable<Models::AccessTier> AccessTier;
/**
* @brief Hash of the blob content. This hash is used to verify the integrity of
* the blob during transport. When this header is specified, the storage service checks the hash
* that has arrived with the one that was sent.
*/
Azure::Nullable<ContentHash> TransactionalContentHash;
};
/**
* @brief Optional parameters for #Azure::Storage::Blobs::BlobClient::AbortCopyFromUri.
*/

View File

@ -1635,6 +1635,41 @@ namespace Azure { namespace Storage { namespace Blobs {
Azure::Nullable<ContentHash> TransactionalContentHash;
}; // struct CommitBlockListResult
/**
* @brief Response type for #Azure::Storage::Blobs::BlobClient::CopyFromUri.
*/
struct CopyBlobFromUriResult final
{
/**
* The ETag contains a value that you can use to perform operations conditionally.
*/
Azure::ETag ETag;
/**
* The date and time the container was last modified. Any operation that modifies the blob,
* including an update of the metadata or properties, changes the last-modified time of the
* blob.
*/
Azure::DateTime LastModified;
/**
* String identifier for the last attempted Copy Blob operation where this blob was the
* destination. This value is null if this blob has never been the destination of a copy
* operation, or if this blob has been modified after a concluded copy operation.
*/
std::string CopyId;
/**
* State of the copy operation identified by the copy ID. Possible values include success,
* pending, aborted, failed etc. This value is null if this blob has never been the
* destination of a copy operation, or if this blob has been modified after a concluded copy
* operation.
*/
Models::CopyStatus CopyStatus;
/**
* A string value that uniquely identifies the blob. This value is null if Blob Versioning is
* not enabled.
*/
Azure::Nullable<std::string> VersionId;
}; // struct CopyBlobFromUriResult
/**
* @brief Response type for #Azure::Storage::Blobs::AppendBlobClient::Create.
*/
@ -7696,6 +7731,153 @@ namespace Azure { namespace Storage { namespace Blobs {
return SetAccessTierCreateResponse(std::move(pHttpResponse), context);
}
struct CopyBlobFromUriOptions final
{
Azure::Nullable<int32_t> Timeout;
Storage::Metadata Metadata;
std::map<std::string, std::string> Tags;
std::string SourceUri;
Azure::Nullable<std::string> LeaseId;
Azure::Nullable<Models::AccessTier> AccessTier;
Azure::Nullable<Azure::DateTime> IfModifiedSince;
Azure::Nullable<Azure::DateTime> IfUnmodifiedSince;
Azure::ETag IfMatch;
Azure::ETag IfNoneMatch;
Azure::Nullable<std::string> IfTags;
Azure::Nullable<Azure::DateTime> SourceIfModifiedSince;
Azure::Nullable<Azure::DateTime> SourceIfUnmodifiedSince;
Azure::ETag SourceIfMatch;
Azure::ETag SourceIfNoneMatch;
Azure::Nullable<ContentHash> TransactionalContentHash;
}; // struct CopyBlobFromUriOptions
static Azure::Response<CopyBlobFromUriResult> CopyFromUri(
Azure::Core::Http::_internal::HttpPipeline& pipeline,
const Azure::Core::Url& url,
const CopyBlobFromUriOptions& options,
const Azure::Core::Context& context)
{
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Put, url);
request.SetHeader("Content-Length", "0");
request.SetHeader("x-ms-requires-sync", "true");
request.SetHeader("x-ms-version", "2020-02-10");
if (options.Timeout.HasValue())
{
request.GetUrl().AppendQueryParameter(
"timeout", std::to_string(options.Timeout.Value()));
}
for (const auto& pair : options.Metadata)
{
request.SetHeader("x-ms-meta-" + pair.first, pair.second);
}
if (!options.Tags.empty())
{
std::string blobTagsValue;
for (const auto& tag : options.Tags)
{
if (!blobTagsValue.empty())
{
blobTagsValue += "&";
}
blobTagsValue += _internal::UrlEncodeQueryParameter(tag.first) + "="
+ _internal::UrlEncodeQueryParameter(tag.second);
}
request.SetHeader("x-ms-tags", std::move(blobTagsValue));
}
request.SetHeader("x-ms-copy-source", options.SourceUri);
if (options.LeaseId.HasValue())
{
request.SetHeader("x-ms-lease-id", options.LeaseId.Value());
}
if (options.AccessTier.HasValue())
{
request.SetHeader("x-ms-access-tier", options.AccessTier.Value().ToString());
}
if (options.IfModifiedSince.HasValue())
{
request.SetHeader(
"If-Modified-Since",
options.IfModifiedSince.Value().ToString(Azure::DateTime::DateFormat::Rfc1123));
}
if (options.IfUnmodifiedSince.HasValue())
{
request.SetHeader(
"If-Unmodified-Since",
options.IfUnmodifiedSince.Value().ToString(Azure::DateTime::DateFormat::Rfc1123));
}
if (options.IfMatch.HasValue() && !options.IfMatch.ToString().empty())
{
request.SetHeader("If-Match", options.IfMatch.ToString());
}
if (options.IfNoneMatch.HasValue() && !options.IfNoneMatch.ToString().empty())
{
request.SetHeader("If-None-Match", options.IfNoneMatch.ToString());
}
if (options.IfTags.HasValue())
{
request.SetHeader("x-ms-if-tags", options.IfTags.Value());
}
if (options.SourceIfModifiedSince.HasValue())
{
request.SetHeader(
"x-ms-source-if-modified-since",
options.SourceIfModifiedSince.Value().ToString(
Azure::DateTime::DateFormat::Rfc1123));
}
if (options.SourceIfUnmodifiedSince.HasValue())
{
request.SetHeader(
"x-ms-source-if-unmodified-since",
options.SourceIfUnmodifiedSince.Value().ToString(
Azure::DateTime::DateFormat::Rfc1123));
}
if (options.SourceIfMatch.HasValue() && !options.SourceIfMatch.ToString().empty())
{
request.SetHeader("x-ms-source-if-match", options.SourceIfMatch.ToString());
}
if (options.SourceIfNoneMatch.HasValue() && !options.SourceIfNoneMatch.ToString().empty())
{
request.SetHeader("x-ms-source-if-none-match", options.SourceIfNoneMatch.ToString());
}
if (options.TransactionalContentHash.HasValue())
{
if (options.TransactionalContentHash.Value().Algorithm == HashAlgorithm::Md5)
{
request.SetHeader(
"x-ms-source-content-md5",
Azure::Core::Convert::Base64Encode(
options.TransactionalContentHash.Value().Value));
}
else if (options.TransactionalContentHash.Value().Algorithm == HashAlgorithm::Crc64)
{
request.SetHeader(
"x-ms-source-content-crc64",
Azure::Core::Convert::Base64Encode(
options.TransactionalContentHash.Value().Value));
}
}
auto pHttpResponse = pipeline.Send(request, context);
Azure::Core::Http::RawResponse& httpResponse = *pHttpResponse;
CopyBlobFromUriResult response;
auto http_status_code = httpResponse.GetStatusCode();
if (http_status_code != Azure::Core::Http::HttpStatusCode::Accepted)
{
throw StorageException::CreateFromResponse(std::move(pHttpResponse));
}
response.ETag = Azure::ETag(httpResponse.GetHeaders().at("etag"));
response.LastModified = Azure::DateTime::Parse(
httpResponse.GetHeaders().at("last-modified"), Azure::DateTime::DateFormat::Rfc1123);
response.CopyId = httpResponse.GetHeaders().at("x-ms-copy-id");
response.CopyStatus = CopyStatus(httpResponse.GetHeaders().at("x-ms-copy-status"));
auto x_ms_version_id__iterator = httpResponse.GetHeaders().find("x-ms-version-id");
if (x_ms_version_id__iterator != httpResponse.GetHeaders().end())
{
response.VersionId = x_ms_version_id__iterator->second;
}
return Azure::Response<CopyBlobFromUriResult>(
std::move(response), std::move(pHttpResponse));
}
struct StartBlobCopyFromUriOptions final
{
Azure::Nullable<int32_t> Timeout;

View File

@ -3,6 +3,7 @@
#include "azure/storage/blobs/blob_client.hpp"
#include <azure/core/azure_assert.hpp>
#include <azure/core/http/policies/policy.hpp>
#include <azure/storage/common/internal/concurrent_transfer.hpp>
#include <azure/storage/common/internal/constants.hpp>
@ -531,6 +532,38 @@ namespace Azure { namespace Storage { namespace Blobs {
*m_pipeline, m_blobUrl, protocolLayerOptions, context);
}
Azure::Response<Models::CopyBlobFromUriResult> BlobClient::CopyFromUri(
const std::string& sourceUri,
const CopyBlobFromUriOptions& options,
const Azure::Core::Context& context) const
{
_detail::BlobRestClient::Blob::CopyBlobFromUriOptions protocolLayerOptions;
protocolLayerOptions.Metadata = options.Metadata;
protocolLayerOptions.Tags = options.Tags;
protocolLayerOptions.SourceUri = sourceUri;
protocolLayerOptions.AccessTier = options.AccessTier;
protocolLayerOptions.LeaseId = options.AccessConditions.LeaseId;
protocolLayerOptions.IfModifiedSince = options.AccessConditions.IfModifiedSince;
protocolLayerOptions.IfUnmodifiedSince = options.AccessConditions.IfUnmodifiedSince;
protocolLayerOptions.IfMatch = options.AccessConditions.IfMatch;
protocolLayerOptions.IfNoneMatch = options.AccessConditions.IfNoneMatch;
protocolLayerOptions.IfTags = options.AccessConditions.TagConditions;
protocolLayerOptions.SourceIfModifiedSince = options.SourceAccessConditions.IfModifiedSince;
protocolLayerOptions.SourceIfUnmodifiedSince = options.SourceAccessConditions.IfUnmodifiedSince;
protocolLayerOptions.SourceIfMatch = options.SourceAccessConditions.IfMatch;
protocolLayerOptions.SourceIfNoneMatch = options.SourceAccessConditions.IfNoneMatch;
if (options.TransactionalContentHash.HasValue())
{
AZURE_ASSERT_MSG(
options.TransactionalContentHash.Value().Algorithm == HashAlgorithm::Md5,
"This operation only supports MD5 transactional content hash.");
protocolLayerOptions.TransactionalContentHash = options.TransactionalContentHash;
}
return _detail::BlobRestClient::Blob::CopyFromUri(
*m_pipeline, m_blobUrl, protocolLayerOptions, context);
}
StartBlobCopyOperation BlobClient::StartCopyFromUri(
const std::string& sourceUri,
const StartBlobCopyFromUriOptions& options,

View File

@ -210,53 +210,6 @@ namespace Azure { namespace Storage { namespace Test {
EXPECT_NO_THROW(appendBlobClient.Delete(options));
}
TEST_F(AppendBlobClientTest, SourceBlobAccessConditions)
{
auto sourceBlobClient = Azure::Storage::Blobs::AppendBlobClient::CreateFromConnectionString(
StandardStorageConnectionString(), m_containerName, RandomString());
auto createResponse = sourceBlobClient.Create();
Azure::ETag eTag = createResponse.Value.ETag;
auto lastModifiedTime = createResponse.Value.LastModified;
auto timeBeforeStr = lastModifiedTime - std::chrono::seconds(1);
auto timeAfterStr = lastModifiedTime + std::chrono::seconds(1);
auto destBlobClient = Azure::Storage::Blobs::AppendBlobClient::CreateFromConnectionString(
StandardStorageConnectionString(), m_containerName, RandomString());
{
Blobs::StartBlobCopyFromUriOptions options;
options.SourceAccessConditions.IfMatch = eTag;
EXPECT_NO_THROW(destBlobClient.StartCopyFromUri(sourceBlobClient.GetUrl(), options));
options.SourceAccessConditions.IfMatch = DummyETag;
EXPECT_THROW(
destBlobClient.StartCopyFromUri(sourceBlobClient.GetUrl(), options), StorageException);
}
{
Blobs::StartBlobCopyFromUriOptions options;
options.SourceAccessConditions.IfNoneMatch = DummyETag;
EXPECT_NO_THROW(destBlobClient.StartCopyFromUri(sourceBlobClient.GetUrl(), options));
options.SourceAccessConditions.IfNoneMatch = eTag;
EXPECT_THROW(
destBlobClient.StartCopyFromUri(sourceBlobClient.GetUrl(), options), StorageException);
}
{
Blobs::StartBlobCopyFromUriOptions options;
options.SourceAccessConditions.IfModifiedSince = timeBeforeStr;
EXPECT_NO_THROW(destBlobClient.StartCopyFromUri(sourceBlobClient.GetUrl(), options));
options.SourceAccessConditions.IfModifiedSince = timeAfterStr;
EXPECT_THROW(
destBlobClient.StartCopyFromUri(sourceBlobClient.GetUrl(), options), StorageException);
}
{
Blobs::StartBlobCopyFromUriOptions options;
options.SourceAccessConditions.IfUnmodifiedSince = timeAfterStr;
EXPECT_NO_THROW(destBlobClient.StartCopyFromUri(sourceBlobClient.GetUrl(), options));
options.SourceAccessConditions.IfUnmodifiedSince = timeBeforeStr;
EXPECT_THROW(
destBlobClient.StartCopyFromUri(sourceBlobClient.GetUrl(), options), StorageException);
}
}
TEST_F(AppendBlobClientTest, Seal)
{
std::string blobName = RandomString();

View File

@ -1127,6 +1127,38 @@ namespace Azure { namespace Storage { namespace Test {
options.AccessConditions.TagConditions = successWhereExpression;
EXPECT_NO_THROW(blockBlobClient.GetBlockList(options));
}
{
auto sourceBlobClient = Azure::Storage::Blobs::BlockBlobClient::CreateFromConnectionString(
StandardStorageConnectionString(), m_containerName, RandomString());
std::vector<uint8_t> buffer;
buffer.resize(1024);
sourceBlobClient.UploadFrom(buffer.data(), buffer.size());
Blobs::CopyBlobFromUriOptions options;
options.AccessConditions.TagConditions = failWhereExpression;
EXPECT_THROW(
blockBlobClient.CopyFromUri(sourceBlobClient.GetUrl() + GetSas(), options),
StorageException);
options.AccessConditions.TagConditions = successWhereExpression;
EXPECT_NO_THROW(blockBlobClient.CopyFromUri(sourceBlobClient.GetUrl() + GetSas(), options));
}
{
auto sourceBlobClient = Azure::Storage::Blobs::BlockBlobClient::CreateFromConnectionString(
StandardStorageConnectionString(), m_containerName, RandomString());
std::vector<uint8_t> buffer;
buffer.resize(1024);
sourceBlobClient.UploadFrom(buffer.data(), buffer.size());
sourceBlobClient.SetTags(tags);
Blobs::StartBlobCopyFromUriOptions options;
options.SourceAccessConditions.TagConditions = failWhereExpression;
EXPECT_THROW(
blockBlobClient.StartCopyFromUri(sourceBlobClient.GetUrl(), options), StorageException);
options.SourceAccessConditions.TagConditions = successWhereExpression;
EXPECT_NO_THROW(blockBlobClient.StartCopyFromUri(sourceBlobClient.GetUrl(), options));
}
}
TEST_F(BlobContainerClientTest, SpecialBlobName)

View File

@ -314,7 +314,39 @@ namespace Azure { namespace Storage { namespace Test {
EXPECT_THROW(blockBlobClient.Download(options), StorageException);
}
TEST_F(BlockBlobClientTest, CopyFromUri)
TEST_F(BlockBlobClientTest, SyncCopyFromUri)
{
const std::string blobName = RandomString();
auto blobClient = m_blobContainerClient->GetBlobClient(blobName);
auto res = blobClient.CopyFromUri(m_blockBlobClient->GetUrl() + GetSas());
EXPECT_EQ(res.RawResponse->GetStatusCode(), Azure::Core::Http::HttpStatusCode::Accepted);
EXPECT_TRUE(res.Value.ETag.HasValue());
EXPECT_TRUE(IsValidTime(res.Value.LastModified));
EXPECT_FALSE(res.Value.CopyId.empty());
EXPECT_EQ(res.Value.CopyStatus, Azure::Storage::Blobs::Models::CopyStatus::Success);
auto downloadResult = blobClient.Download();
EXPECT_FALSE(downloadResult.Value.Details.CopyId.Value().empty());
EXPECT_FALSE(downloadResult.Value.Details.CopySource.Value().empty());
EXPECT_TRUE(
downloadResult.Value.Details.CopyStatus.Value()
== Azure::Storage::Blobs::Models::CopyStatus::Success);
EXPECT_FALSE(downloadResult.Value.Details.CopyProgress.Value().empty());
EXPECT_TRUE(IsValidTime(downloadResult.Value.Details.CopyCompletedOn.Value()));
auto blobItem = GetBlobItem(blobName, Blobs::Models::ListBlobsIncludeFlags::Copy);
EXPECT_FALSE(blobItem.Details.CopyId.Value().empty());
EXPECT_FALSE(blobItem.Details.CopySource.Value().empty());
EXPECT_TRUE(
blobItem.Details.CopyStatus.Value() == Azure::Storage::Blobs::Models::CopyStatus::Success);
EXPECT_FALSE(blobItem.Details.CopyProgress.Value().empty());
EXPECT_TRUE(IsValidTime(blobItem.Details.CopyCompletedOn.Value()));
ASSERT_TRUE(blobItem.Details.IsIncrementalCopy.HasValue());
EXPECT_FALSE(blobItem.Details.IsIncrementalCopy.Value());
EXPECT_FALSE(blobItem.Details.IncrementalCopyDestinationSnapshot.HasValue());
}
TEST_F(BlockBlobClientTest, AsyncCopyFromUri)
{
const std::string blobName = RandomString();
auto blobClient = m_blobContainerClient->GetBlobClient(blobName);
@ -353,15 +385,32 @@ namespace Azure { namespace Storage { namespace Test {
EXPECT_FALSE(blobItem.Details.IncrementalCopyDestinationSnapshot.HasValue());
}
TEST_F(BlockBlobClientTest, CopyWithTags)
TEST_F(BlockBlobClientTest, CopyWithTagsMetadataTier)
{
auto blobClient = m_blobContainerClient->GetBlockBlobClient(RandomString());
Blobs::StartBlobCopyFromUriOptions options;
options.Tags["key1"] = "value1";
options.Tags["key2"] = "value2";
options.Tags["key3 +-./:=_"] = "v1 +-./:=_";
blobClient.StartCopyFromUri(m_blockBlobClient->GetUrl(), options);
options.Metadata["key1"] = "value1";
options.Metadata["key2"] = "value2";
options.AccessTier = Blobs::Models::AccessTier::Cool;
auto operation = blobClient.StartCopyFromUri(m_blockBlobClient->GetUrl(), options);
operation.PollUntilDone(std::chrono::seconds(1));
EXPECT_EQ(blobClient.GetTags().Value, options.Tags);
auto properties = blobClient.GetProperties().Value;
EXPECT_EQ(properties.Metadata, options.Metadata);
EXPECT_EQ(properties.AccessTier.Value(), options.AccessTier.Value());
Blobs::CopyBlobFromUriOptions options2;
options2.Tags = options.Tags;
options2.Metadata = options.Metadata;
options2.AccessTier = options.AccessTier;
blobClient.CopyFromUri(m_blockBlobClient->GetUrl() + GetSas(), options2);
EXPECT_EQ(blobClient.GetTags().Value, options2.Tags);
properties = blobClient.GetProperties().Value;
EXPECT_EQ(properties.Metadata, options2.Metadata);
EXPECT_EQ(properties.AccessTier.Value(), options2.AccessTier.Value());
}
TEST_F(BlockBlobClientTest, SnapShotVersions)
@ -1170,4 +1219,127 @@ namespace Azure { namespace Storage { namespace Test {
EXPECT_EQ(blobItem.BlobSize, 0);
}
TEST_F(BlobContainerClientTest, SourceTagsConditions)
{
auto sourceBlobClient = Azure::Storage::Blobs::BlockBlobClient::CreateFromConnectionString(
StandardStorageConnectionString(), m_containerName, RandomString());
std::vector<uint8_t> buffer;
buffer.resize(1024);
}
TEST_F(BlobContainerClientTest, SourceBlobAccessConditions)
{
auto sourceBlobClient = Azure::Storage::Blobs::BlockBlobClient::CreateFromConnectionString(
StandardStorageConnectionString(), m_containerName, RandomString());
std::vector<uint8_t> buffer;
buffer.resize(1024);
auto createResponse = sourceBlobClient.UploadFrom(buffer.data(), buffer.size());
Azure::ETag eTag = createResponse.Value.ETag;
auto lastModifiedTime = createResponse.Value.LastModified;
auto timeBeforeStr = lastModifiedTime - std::chrono::seconds(2);
auto timeAfterStr = lastModifiedTime + std::chrono::seconds(2);
auto destBlobClient = Azure::Storage::Blobs::BlockBlobClient::CreateFromConnectionString(
StandardStorageConnectionString(), m_containerName, RandomString());
{
Blobs::StartBlobCopyFromUriOptions options;
options.SourceAccessConditions.IfMatch = eTag;
EXPECT_NO_THROW(destBlobClient.StartCopyFromUri(sourceBlobClient.GetUrl(), options));
options.SourceAccessConditions.IfMatch = DummyETag;
EXPECT_THROW(
destBlobClient.StartCopyFromUri(sourceBlobClient.GetUrl(), options), StorageException);
Blobs::CopyBlobFromUriOptions options2;
options2.SourceAccessConditions.IfMatch = eTag;
EXPECT_NO_THROW(destBlobClient.CopyFromUri(sourceBlobClient.GetUrl() + GetSas(), options2));
options2.SourceAccessConditions.IfMatch = DummyETag;
EXPECT_THROW(
destBlobClient.CopyFromUri(sourceBlobClient.GetUrl() + GetSas(), options2),
StorageException);
}
{
Blobs::StartBlobCopyFromUriOptions options;
options.SourceAccessConditions.IfNoneMatch = DummyETag;
EXPECT_NO_THROW(destBlobClient.StartCopyFromUri(sourceBlobClient.GetUrl(), options));
options.SourceAccessConditions.IfNoneMatch = eTag;
EXPECT_THROW(
destBlobClient.StartCopyFromUri(sourceBlobClient.GetUrl(), options), StorageException);
Blobs::CopyBlobFromUriOptions options2;
options2.SourceAccessConditions.IfNoneMatch = DummyETag;
EXPECT_NO_THROW(destBlobClient.CopyFromUri(sourceBlobClient.GetUrl() + GetSas(), options2));
options2.SourceAccessConditions.IfNoneMatch = eTag;
EXPECT_THROW(
destBlobClient.CopyFromUri(sourceBlobClient.GetUrl() + GetSas(), options2),
StorageException);
}
{
Blobs::StartBlobCopyFromUriOptions options;
options.SourceAccessConditions.IfModifiedSince = timeBeforeStr;
EXPECT_NO_THROW(destBlobClient.StartCopyFromUri(sourceBlobClient.GetUrl(), options));
options.SourceAccessConditions.IfModifiedSince = timeAfterStr;
EXPECT_THROW(
destBlobClient.StartCopyFromUri(sourceBlobClient.GetUrl(), options), StorageException);
sourceBlobClient.GetProperties();
Blobs::CopyBlobFromUriOptions options2;
options2.SourceAccessConditions.IfModifiedSince = timeBeforeStr;
EXPECT_NO_THROW(destBlobClient.CopyFromUri(sourceBlobClient.GetUrl() + GetSas(), options2));
options2.SourceAccessConditions.IfModifiedSince = timeAfterStr;
EXPECT_THROW(
destBlobClient.CopyFromUri(sourceBlobClient.GetUrl() + GetSas(), options2),
StorageException);
}
{
Blobs::StartBlobCopyFromUriOptions options;
options.SourceAccessConditions.IfUnmodifiedSince = timeAfterStr;
EXPECT_NO_THROW(destBlobClient.StartCopyFromUri(sourceBlobClient.GetUrl(), options));
options.SourceAccessConditions.IfUnmodifiedSince = timeBeforeStr;
EXPECT_THROW(
destBlobClient.StartCopyFromUri(sourceBlobClient.GetUrl(), options), StorageException);
Blobs::CopyBlobFromUriOptions options2;
options2.SourceAccessConditions.IfUnmodifiedSince = timeAfterStr;
EXPECT_NO_THROW(destBlobClient.CopyFromUri(sourceBlobClient.GetUrl() + GetSas(), options2));
options2.SourceAccessConditions.IfUnmodifiedSince = timeBeforeStr;
EXPECT_THROW(
destBlobClient.CopyFromUri(sourceBlobClient.GetUrl() + GetSas(), options2),
StorageException);
}
// lease
{
const std::string leaseId = Blobs::BlobLeaseClient::CreateUniqueLeaseId();
const std::string dummyLeaseId = Blobs::BlobLeaseClient::CreateUniqueLeaseId();
Blobs::BlobLeaseClient leaseClient(destBlobClient, leaseId);
leaseClient.Acquire(std::chrono::seconds(60));
Blobs::CopyBlobFromUriOptions options;
options.AccessConditions.LeaseId = dummyLeaseId;
EXPECT_THROW(
destBlobClient.CopyFromUri(sourceBlobClient.GetUrl() + GetSas(), options),
StorageException);
options.AccessConditions.LeaseId = leaseId;
EXPECT_NO_THROW(destBlobClient.CopyFromUri(sourceBlobClient.GetUrl() + GetSas(), options));
leaseClient.Release();
}
// content md5
{
const auto hash = sourceBlobClient.GetProperties().Value.HttpHeaders.ContentHash;
ASSERT_FALSE(hash.Value.empty());
Blobs::CopyBlobFromUriOptions options;
options.TransactionalContentHash = hash;
options.TransactionalContentHash.Value().Value = Azure::Core::Convert::Base64Decode(DummyMd5);
EXPECT_THROW(
destBlobClient.CopyFromUri(sourceBlobClient.GetUrl() + GetSas(), options),
StorageException);
options.TransactionalContentHash = hash;
EXPECT_NO_THROW(destBlobClient.CopyFromUri(sourceBlobClient.GetUrl() + GetSas(), options));
}
}
}}} // namespace Azure::Storage::Test