Some API changes to blob service (#2557)

This commit is contained in:
JinmingHu 2021-07-09 11:26:36 +08:00 committed by GitHub
parent a97970ea20
commit bc16d4d812
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 962 additions and 184 deletions

View File

@ -4,10 +4,20 @@
### Features Added
- Added lease ID access condition and tags access condition for `BlobClient::SetAccessTier()`.
- Added source ETag access conditions and last-modified access conditions for `PageBlobClient::UploadPagesFromUri()`.
- Added three new fields `IsServerEncrypted`, `EncryptionKeySha256` and `EncryptionScope` into `SetBlobMetadataResult`.
- Added support for setting blob tags when creating or copying blobs.
- Added new fields `AccessTierChangedOn`, `ArchiveStatus`, `RehydratePriority`, `CopyId`, `CopySource`, `CopyStatus`, `CopyStatusDescription`, `IsIncrementalCopy`, `IncrementalCopyDestinationSnapshot`, `CopyProgress`, `CopyCompletedOn`, `TagCount`, `Tags`, `DeletedOn` and `RemainingRetentionDays` into `BlobItemDetails`.
- Added support for including blob tags when listing blobs.
### Breaking Changes
### Bugs Fixed
- Fixed a bug where lease ID didn't work for `BlobContainerClient::GetAccessPolicy()`.
- Fixed a bug where `BlobItemDetails::EncryptionKeySha256` was always null because it wasn't correctly parsed from xml.
### Other Changes
## 12.0.1 (2021-07-07)

View File

@ -435,6 +435,12 @@ namespace Azure { namespace Storage { namespace Blobs {
* same blob.
*/
Azure::Nullable<Models::RehydratePriority> RehydratePriority;
/**
* @brief Optional conditions that must be met to perform this operation.
*/
struct : public LeaseAccessConditions, public TagAccessConditions
{
} AccessConditions;
};
/**
@ -451,6 +457,11 @@ namespace Azure { namespace Storage { namespace Blobs {
*/
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.
*/
@ -458,8 +469,15 @@ namespace Azure { namespace Storage { namespace Blobs {
/**
* @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.
*/
BlobAccessConditions SourceAccessConditions;
struct : public Azure::ModifiedConditions,
public Azure::MatchConditions,
public LeaseAccessConditions,
public TagAccessConditions
{
} SourceAccessConditions;
/**
* @brief Specifies the tier to be set on the target blob.
@ -698,6 +716,11 @@ namespace Azure { namespace Storage { namespace Blobs {
*/
Storage::Metadata Metadata;
/**
* @brief The tags to set for this blob.
*/
std::map<std::string, std::string> Tags;
/**
* @brief Indicates the tier to be set on blob.
*/
@ -724,6 +747,11 @@ namespace Azure { namespace Storage { namespace Blobs {
*/
Storage::Metadata Metadata;
/**
* @brief The tags to set for this blob.
*/
std::map<std::string, std::string> Tags;
/**
* @brief Indicates the tier to be set on blob.
*/
@ -816,6 +844,11 @@ namespace Azure { namespace Storage { namespace Blobs {
*/
Storage::Metadata Metadata;
/**
* @brief The tags to set for this blob.
*/
std::map<std::string, std::string> Tags;
/**
* @brief Indicates the tier to be set on blob.
*/
@ -861,6 +894,11 @@ namespace Azure { namespace Storage { namespace Blobs {
*/
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.
*/
@ -945,6 +983,11 @@ namespace Azure { namespace Storage { namespace Blobs {
*/
Azure::Nullable<Models::AccessTier> AccessTier;
/**
* @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.
*/
@ -985,6 +1028,13 @@ namespace Azure { namespace Storage { namespace Blobs {
* @brief Optional conditions that must be met to perform this operation.
*/
PageBlobAccessConditions AccessConditions;
/**
* @brief Optional conditions that the source must meet to perform this operation.
*/
struct : public Azure::ModifiedConditions, public Azure::MatchConditions
{
} SourceAccessConditions;
};
/**

View File

@ -846,6 +846,18 @@ namespace Azure { namespace Storage { namespace Blobs {
* True if the access tier is not explicitly set on the blob.
*/
Azure::Nullable<bool> IsAccessTierInferred;
/**
* The date and time the tier was changed on the object.
*/
Azure::Nullable<Azure::DateTime> AccessTierChangedOn;
/**
* Indicates if the blob is being rehydrated.
*/
Azure::Nullable<Models::ArchiveStatus> ArchiveStatus;
/**
* Priority of rehydrate if the blob is being rehydrated.
*/
Azure::Nullable<Models::RehydratePriority> RehydratePriority;
/**
* The current lease status of the blob.
*/
@ -886,6 +898,66 @@ namespace Azure { namespace Storage { namespace Blobs {
* Only valid when Object Replication is enabled and current blob is the source.
*/
std::vector<ObjectReplicationPolicy> ObjectReplicationSourceProperties;
/**
* 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.
*/
Azure::Nullable<std::string> CopyId;
/**
* URL that specifies the source blob or file used in the last attempted copy operation where
* this blob was the destination blob. 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.
*/
Azure::Nullable<std::string> CopySource;
/**
* 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.
*/
Azure::Nullable<Models::CopyStatus> CopyStatus;
/**
* Describes the cause of the last fatal or non-fatal copy operation failure. This is not null
* only when copy status is failed or pending.
*/
Azure::Nullable<std::string> CopyStatusDescription;
/**
* True if the copy operation is incremental copy.
*/
Azure::Nullable<bool> IsIncrementalCopy;
/**
* Snapshot time of the last successful incremental copy snapshot for this blob.
*/
Azure::Nullable<std::string> IncrementalCopyDestinationSnapshot;
/**
* Contains the number of bytes copied and the total bytes in the source in the last attempted
* copy operation where this blob was the destination blob.
*/
Azure::Nullable<std::string> CopyProgress;
/**
* Conclusion time of the last attempted copy operation where this blob was the destination
* blob.
*/
Azure::Nullable<Azure::DateTime> CopyCompletedOn;
/**
* the number of tags stored on the blob.
*/
Azure::Nullable<int32_t> TagCount;
/**
* User-defined tags for this blob.
*/
std::map<std::string, std::string> Tags;
/**
* Data and time at which this blob was deleted. Only valid when this blob was deleted.
*/
Azure::Nullable<Azure::DateTime> DeletedOn;
/**
* Remaining days before this blob will be permanantely deleted. Only valid when this blob was
* deleted.
*/
Azure::Nullable<int32_t> RemainingRetentionDays;
}; // struct BlobItemDetails
/**
@ -1921,6 +1993,10 @@ namespace Azure { namespace Storage { namespace Blobs {
* Uncommitted blobs should be included.
*/
UncomittedBlobs = 32,
/**
* Tags should be included.
*/
Tags = 64,
}; // bitwise enum ListBlobsIncludeFlags
inline ListBlobsIncludeFlags operator|(ListBlobsIncludeFlags lhs, ListBlobsIncludeFlags rhs)
@ -2082,6 +2158,16 @@ namespace Azure { namespace Storage { namespace Blobs {
*/
struct SetBlobExpiryResult 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;
}; // struct SetBlobExpiryResult
/**
@ -2122,10 +2208,23 @@ namespace Azure { namespace Storage { namespace Blobs {
*/
Azure::DateTime LastModified;
/**
* The current sequence number for a page blob. This value is null for block blobs or append
* blobs.
* This value is always null, don't use it.
*/
Azure::Nullable<int64_t> SequenceNumber;
/**
* True if the blob data and metadata are completely encrypted using the specified algorithm.
* Otherwise, the value is set to false (when the blob is unencrypted, or if only parts of the
* blob/application metadata are encrypted).
*/
bool IsServerEncrypted = false;
/**
* The SHA-256 hash of the encryption key used to encrypt the blob data and metadata.
*/
Azure::Nullable<std::vector<uint8_t>> EncryptionKeySha256;
/**
* Name of the encryption scope used to encrypt the blob data and metadata.
*/
Azure::Nullable<std::string> EncryptionScope;
}; // struct SetBlobMetadataResult
/**
@ -2486,6 +2585,9 @@ namespace Azure { namespace Storage { namespace Blobs {
namespace _detail {
struct GetBlobTagsResult final
{
/**
* User-defined tags for this blob.
*/
std::map<std::string, std::string> Tags;
}; // struct GetBlobTagsResult
} // namespace _detail
@ -2575,11 +2677,6 @@ namespace Azure { namespace Storage { namespace Blobs {
* blob.
*/
Azure::DateTime LastModified;
/**
* The current sequence number for a page blob. This value is null for block blobs or append
* blobs.
*/
Azure::Nullable<int64_t> SequenceNumber;
}; // struct ReleaseBlobLeaseResult
} // namespace _detail
@ -2745,6 +2842,7 @@ namespace Azure { namespace Storage { namespace Blobs {
ListBlobsIncludeFlags::Snapshots,
ListBlobsIncludeFlags::Versions,
ListBlobsIncludeFlags::UncomittedBlobs,
ListBlobsIncludeFlags::Tags,
};
const char* string_list[] = {
"copy",
@ -2753,6 +2851,7 @@ namespace Azure { namespace Storage { namespace Blobs {
"snapshots",
"versions",
"uncommittedblobs",
"tags",
};
std::string ret;
for (size_t i = 0; i < sizeof(value_list) / sizeof(ListBlobsIncludeFlags); ++i)
@ -4986,6 +5085,10 @@ namespace Azure { namespace Storage { namespace Blobs {
}
request.GetUrl().AppendQueryParameter("restype", "container");
request.GetUrl().AppendQueryParameter("comp", "acl");
if (options.LeaseId.HasValue())
{
request.SetHeader("x-ms-lease-id", options.LeaseId.Value());
}
auto pHttpResponse = pipeline.Send(request, context);
Azure::Core::Http::RawResponse& httpResponse = *pHttpResponse;
BlobContainerAccessPolicy response;
@ -5687,15 +5790,32 @@ namespace Azure { namespace Storage { namespace Blobs {
k_BlobType,
k_AccessTier,
k_AccessTierInferred,
k_AccessTierChangeTime,
k_ArchiveStatus,
k_RehydratePriority,
k_LeaseStatus,
k_LeaseState,
k_LeaseDuration,
k_ServerEncrypted,
k_EncryptionKeySHA256,
k_CustomerProvidedKeySha256,
k_EncryptionScope,
k_Sealed,
k_xmsblobsequencenumber,
k_CopyId,
k_CopyStatus,
k_CopySource,
k_CopyProgress,
k_CopyCompletionTime,
k_CopyStatusDescription,
k_IncrementalCopy,
k_CopyDestinationSnapshot,
k_DeletedTime,
k_RemainingRetentionDays,
k_TagCount,
k_Metadata,
k_OrMetadata,
k_Tags,
k_TagSet,
k_Unknown,
};
std::vector<XmlTagName> path;
@ -5803,6 +5923,18 @@ namespace Azure { namespace Storage { namespace Blobs {
{
path.emplace_back(XmlTagName::k_AccessTierInferred);
}
else if (node.Name == "AccessTierChangeTime")
{
path.emplace_back(XmlTagName::k_AccessTierChangeTime);
}
else if (node.Name == "ArchiveStatus")
{
path.emplace_back(XmlTagName::k_ArchiveStatus);
}
else if (node.Name == "RehydratePriority")
{
path.emplace_back(XmlTagName::k_RehydratePriority);
}
else if (node.Name == "LeaseStatus")
{
path.emplace_back(XmlTagName::k_LeaseStatus);
@ -5819,9 +5951,13 @@ namespace Azure { namespace Storage { namespace Blobs {
{
path.emplace_back(XmlTagName::k_ServerEncrypted);
}
else if (node.Name == "EncryptionKeySHA256")
else if (node.Name == "CustomerProvidedKeySha256")
{
path.emplace_back(XmlTagName::k_EncryptionKeySHA256);
path.emplace_back(XmlTagName::k_CustomerProvidedKeySha256);
}
else if (node.Name == "EncryptionScope")
{
path.emplace_back(XmlTagName::k_EncryptionScope);
}
else if (node.Name == "Sealed")
{
@ -5831,6 +5967,50 @@ namespace Azure { namespace Storage { namespace Blobs {
{
path.emplace_back(XmlTagName::k_xmsblobsequencenumber);
}
else if (node.Name == "CopyId")
{
path.emplace_back(XmlTagName::k_CopyId);
}
else if (node.Name == "CopyStatus")
{
path.emplace_back(XmlTagName::k_CopyStatus);
}
else if (node.Name == "CopySource")
{
path.emplace_back(XmlTagName::k_CopySource);
}
else if (node.Name == "CopyProgress")
{
path.emplace_back(XmlTagName::k_CopyProgress);
}
else if (node.Name == "CopyCompletionTime")
{
path.emplace_back(XmlTagName::k_CopyCompletionTime);
}
else if (node.Name == "CopyStatusDescription")
{
path.emplace_back(XmlTagName::k_CopyStatusDescription);
}
else if (node.Name == "IncrementalCopy")
{
path.emplace_back(XmlTagName::k_IncrementalCopy);
}
else if (node.Name == "CopyDestinationSnapshot")
{
path.emplace_back(XmlTagName::k_CopyDestinationSnapshot);
}
else if (node.Name == "DeletedTime")
{
path.emplace_back(XmlTagName::k_DeletedTime);
}
else if (node.Name == "RemainingRetentionDays")
{
path.emplace_back(XmlTagName::k_RemainingRetentionDays);
}
else if (node.Name == "TagCount")
{
path.emplace_back(XmlTagName::k_TagCount);
}
else if (node.Name == "Metadata")
{
path.emplace_back(XmlTagName::k_Metadata);
@ -5839,6 +6019,14 @@ namespace Azure { namespace Storage { namespace Blobs {
{
path.emplace_back(XmlTagName::k_OrMetadata);
}
else if (node.Name == "Tags")
{
path.emplace_back(XmlTagName::k_Tags);
}
else if (node.Name == "TagSet")
{
path.emplace_back(XmlTagName::k_TagSet);
}
else
{
path.emplace_back(XmlTagName::k_Unknown);
@ -5854,6 +6042,13 @@ namespace Azure { namespace Storage { namespace Blobs {
= ObjectReplicationSourcePropertiesFromXml(reader);
path.pop_back();
}
else if (
path.size() == 2 && path[0] == XmlTagName::k_Tags
&& path[1] == XmlTagName::k_TagSet)
{
ret.Details.Tags = TagsFromXml(reader);
path.pop_back();
}
}
else if (node.Type == _internal::XmlNodeType::Text)
{
@ -5972,6 +6167,25 @@ namespace Azure { namespace Storage { namespace Blobs {
{
ret.Details.IsAccessTierInferred = node.Value == "true";
}
else if (
path.size() == 2 && path[0] == XmlTagName::k_Properties
&& path[1] == XmlTagName::k_AccessTierChangeTime)
{
ret.Details.AccessTierChangedOn
= Azure::DateTime::Parse(node.Value, Azure::DateTime::DateFormat::Rfc1123);
}
else if (
path.size() == 2 && path[0] == XmlTagName::k_Properties
&& path[1] == XmlTagName::k_ArchiveStatus)
{
ret.Details.ArchiveStatus = ArchiveStatus(node.Value);
}
else if (
path.size() == 2 && path[0] == XmlTagName::k_Properties
&& path[1] == XmlTagName::k_RehydratePriority)
{
ret.Details.RehydratePriority = RehydratePriority(node.Value);
}
else if (
path.size() == 2 && path[0] == XmlTagName::k_Properties
&& path[1] == XmlTagName::k_LeaseStatus)
@ -5998,10 +6212,16 @@ namespace Azure { namespace Storage { namespace Blobs {
}
else if (
path.size() == 2 && path[0] == XmlTagName::k_Properties
&& path[1] == XmlTagName::k_EncryptionKeySHA256)
&& path[1] == XmlTagName::k_CustomerProvidedKeySha256)
{
ret.Details.EncryptionKeySha256 = Azure::Core::Convert::Base64Decode(node.Value);
}
else if (
path.size() == 2 && path[0] == XmlTagName::k_Properties
&& path[1] == XmlTagName::k_EncryptionScope)
{
ret.Details.EncryptionScope = node.Value;
}
else if (
path.size() == 2 && path[0] == XmlTagName::k_Properties
&& path[1] == XmlTagName::k_Sealed)
@ -6014,6 +6234,74 @@ namespace Azure { namespace Storage { namespace Blobs {
{
ret.Details.SequenceNumber = std::stoll(node.Value);
}
else if (
path.size() == 2 && path[0] == XmlTagName::k_Properties
&& path[1] == XmlTagName::k_CopyId)
{
ret.Details.CopyId = node.Value;
}
else if (
path.size() == 2 && path[0] == XmlTagName::k_Properties
&& path[1] == XmlTagName::k_CopyStatus)
{
ret.Details.CopyStatus = CopyStatus(node.Value);
}
else if (
path.size() == 2 && path[0] == XmlTagName::k_Properties
&& path[1] == XmlTagName::k_CopySource)
{
ret.Details.CopySource = node.Value;
}
else if (
path.size() == 2 && path[0] == XmlTagName::k_Properties
&& path[1] == XmlTagName::k_CopyProgress)
{
ret.Details.CopyProgress = node.Value;
}
else if (
path.size() == 2 && path[0] == XmlTagName::k_Properties
&& path[1] == XmlTagName::k_CopyCompletionTime)
{
ret.Details.CopyCompletedOn
= Azure::DateTime::Parse(node.Value, Azure::DateTime::DateFormat::Rfc1123);
}
else if (
path.size() == 2 && path[0] == XmlTagName::k_Properties
&& path[1] == XmlTagName::k_CopyStatusDescription)
{
ret.Details.CopyStatusDescription = node.Value;
}
else if (
path.size() == 2 && path[0] == XmlTagName::k_Properties
&& path[1] == XmlTagName::k_IncrementalCopy)
{
ret.Details.IsIncrementalCopy = node.Value == "true";
}
else if (
path.size() == 2 && path[0] == XmlTagName::k_Properties
&& path[1] == XmlTagName::k_CopyDestinationSnapshot)
{
ret.Details.IncrementalCopyDestinationSnapshot = node.Value;
}
else if (
path.size() == 2 && path[0] == XmlTagName::k_Properties
&& path[1] == XmlTagName::k_DeletedTime)
{
ret.Details.DeletedOn
= Azure::DateTime::Parse(node.Value, Azure::DateTime::DateFormat::Rfc1123);
}
else if (
path.size() == 2 && path[0] == XmlTagName::k_Properties
&& path[1] == XmlTagName::k_RemainingRetentionDays)
{
ret.Details.RemainingRetentionDays = std::stoi(node.Value);
}
else if (
path.size() == 2 && path[0] == XmlTagName::k_Properties
&& path[1] == XmlTagName::k_TagCount)
{
ret.Details.TagCount = std::stoi(node.Value);
}
}
}
return ret;
@ -6108,6 +6396,56 @@ namespace Azure { namespace Storage { namespace Blobs {
return ret;
}
static std::map<std::string, std::string> TagsFromXml(_internal::XmlReader& reader)
{
std::map<std::string, std::string> ret;
int depth = 0;
std::string key;
bool is_key = false;
bool is_value = false;
while (true)
{
auto node = reader.Read();
if (node.Type == _internal::XmlNodeType::End)
{
break;
}
else if (node.Type == _internal::XmlNodeType::StartTag)
{
++depth;
if (node.Name == "Key")
{
is_key = true;
}
else if (node.Name == "Value")
{
is_value = true;
}
}
else if (node.Type == _internal::XmlNodeType::EndTag)
{
if (depth-- == 0)
{
break;
}
}
if (depth == 2 && node.Type == _internal::XmlNodeType::Text)
{
if (is_key)
{
key = node.Value;
is_key = false;
}
else if (is_value)
{
ret.emplace(std::move(key), node.Value);
is_value = false;
}
}
}
return ret;
}
static std::vector<ObjectReplicationPolicy> ObjectReplicationSourcePropertiesFromXml(
_internal::XmlReader& reader)
{
@ -6727,6 +7065,9 @@ namespace Azure { namespace Storage { namespace Blobs {
{
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);
return Azure::Response<SetBlobExpiryResult>(
std::move(response), std::move(pHttpResponse));
}
@ -7294,6 +7635,27 @@ namespace Azure { namespace Storage { namespace Blobs {
response.ETag = Azure::ETag(httpResponse.GetHeaders().at("etag"));
response.LastModified = Azure::DateTime::Parse(
httpResponse.GetHeaders().at("last-modified"), Azure::DateTime::DateFormat::Rfc1123);
auto x_ms_blob_sequence_number__iterator
= httpResponse.GetHeaders().find("x-ms-blob-sequence-number");
if (x_ms_blob_sequence_number__iterator != httpResponse.GetHeaders().end())
{
response.SequenceNumber = std::stoll(x_ms_blob_sequence_number__iterator->second);
}
response.IsServerEncrypted
= httpResponse.GetHeaders().at("x-ms-request-server-encrypted") == "true";
auto x_ms_encryption_key_sha256__iterator
= httpResponse.GetHeaders().find("x-ms-encryption-key-sha256");
if (x_ms_encryption_key_sha256__iterator != httpResponse.GetHeaders().end())
{
response.EncryptionKeySha256
= Azure::Core::Convert::Base64Decode(x_ms_encryption_key_sha256__iterator->second);
}
auto x_ms_encryption_scope__iterator
= httpResponse.GetHeaders().find("x-ms-encryption-scope");
if (x_ms_encryption_scope__iterator != httpResponse.GetHeaders().end())
{
response.EncryptionScope = x_ms_encryption_scope__iterator->second;
}
return Azure::Response<SetBlobMetadataResult>(
std::move(response), std::move(pHttpResponse));
}
@ -7303,6 +7665,7 @@ namespace Azure { namespace Storage { namespace Blobs {
Azure::Nullable<int32_t> Timeout;
Models::AccessTier AccessTier;
Azure::Nullable<Models::RehydratePriority> RehydratePriority;
Azure::Nullable<std::string> LeaseId;
Azure::Nullable<std::string> IfTags;
}; // struct SetBlobAccessTierOptions
@ -7326,6 +7689,10 @@ namespace Azure { namespace Storage { namespace Blobs {
request.SetHeader(
"x-ms-rehydrate-priority", options.RehydratePriority.Value().ToString());
}
if (options.LeaseId.HasValue())
{
request.SetHeader("x-ms-lease-id", options.LeaseId.Value());
}
if (options.IfTags.HasValue())
{
request.SetHeader("x-ms-if-tags", options.IfTags.Value());
@ -7366,9 +7733,9 @@ namespace Azure { namespace Storage { namespace Blobs {
{
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<std::string> SourceLeaseId;
Azure::Nullable<Models::AccessTier> AccessTier;
Azure::Nullable<Models::RehydratePriority> RehydratePriority;
Azure::Nullable<Azure::DateTime> IfModifiedSince;
@ -7381,6 +7748,7 @@ namespace Azure { namespace Storage { namespace Blobs {
Azure::ETag SourceIfMatch;
Azure::ETag SourceIfNoneMatch;
Azure::Nullable<std::string> SourceIfTags;
Azure::Nullable<std::string> SourceLeaseId;
Azure::Nullable<bool> ShouldSealDestination;
}; // struct StartBlobCopyFromUriOptions
@ -7403,15 +7771,25 @@ namespace Azure { namespace Storage { namespace Blobs {
{
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.SourceLeaseId.HasValue())
{
request.SetHeader("x-ms-source-lease-id", options.SourceLeaseId.Value());
}
if (options.AccessTier.HasValue())
{
request.SetHeader("x-ms-access-tier", options.AccessTier.Value().ToString());
@ -7476,6 +7854,10 @@ namespace Azure { namespace Storage { namespace Blobs {
{
request.SetHeader("x-ms-source-if-tags", options.SourceIfTags.Value());
}
if (options.SourceLeaseId.HasValue())
{
request.SetHeader("x-ms-source-lease-id", options.SourceLeaseId.Value());
}
auto pHttpResponse = pipeline.Send(request, context);
Azure::Core::Http::RawResponse& httpResponse = *pHttpResponse;
Models::_detail::StartBlobCopyFromUriResult response;
@ -8047,12 +8429,6 @@ namespace Azure { namespace Storage { namespace Blobs {
response.ETag = Azure::ETag(httpResponse.GetHeaders().at("etag"));
response.LastModified = Azure::DateTime::Parse(
httpResponse.GetHeaders().at("last-modified"), Azure::DateTime::DateFormat::Rfc1123);
auto x_ms_blob_sequence_number__iterator
= httpResponse.GetHeaders().find("x-ms-blob-sequence-number");
if (x_ms_blob_sequence_number__iterator != httpResponse.GetHeaders().end())
{
response.SequenceNumber = std::stoll(x_ms_blob_sequence_number__iterator->second);
}
return Azure::Response<Models::_detail::ReleaseBlobLeaseResult>(
std::move(response), std::move(pHttpResponse));
}
@ -8271,6 +8647,7 @@ namespace Azure { namespace Storage { namespace Blobs {
Azure::Nullable<ContentHash> TransactionalContentHash;
BlobHttpHeaders HttpHeaders;
Storage::Metadata Metadata;
std::map<std::string, std::string> Tags;
Azure::Nullable<std::string> LeaseId;
Azure::Nullable<Models::AccessTier> AccessTier;
Azure::Nullable<std::string> EncryptionKey;
@ -8368,6 +8745,20 @@ namespace Azure { namespace Storage { namespace Blobs {
{
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));
}
if (options.LeaseId.HasValue())
{
request.SetHeader("x-ms-lease-id", options.LeaseId.Value());
@ -8741,6 +9132,7 @@ namespace Azure { namespace Storage { namespace Blobs {
std::vector<std::pair<BlockType, std::string>> BlockList;
BlobHttpHeaders HttpHeaders;
Storage::Metadata Metadata;
std::map<std::string, std::string> Tags;
Azure::Nullable<std::string> LeaseId;
Azure::Nullable<std::string> EncryptionKey;
Azure::Nullable<std::vector<uint8_t>> EncryptionKeySha256;
@ -8811,6 +9203,20 @@ namespace Azure { namespace Storage { namespace Blobs {
{
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));
}
if (options.LeaseId.HasValue())
{
request.SetHeader("x-ms-lease-id", options.LeaseId.Value());
@ -9122,6 +9528,7 @@ namespace Azure { namespace Storage { namespace Blobs {
Azure::ETag IfMatch;
Azure::ETag IfNoneMatch;
Azure::Nullable<std::string> IfTags;
std::map<std::string, std::string> Tags;
}; // struct CreatePageBlobOptions
static Azure::Response<CreatePageBlobResult> Create(
@ -9170,6 +9577,20 @@ namespace Azure { namespace Storage { namespace Blobs {
{
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));
}
if (options.LeaseId.HasValue())
{
request.SetHeader("x-ms-lease-id", options.LeaseId.Value());
@ -9467,6 +9888,10 @@ namespace Azure { namespace Storage { namespace Blobs {
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;
}; // struct UploadPageBlobPagesFromUriOptions
static Azure::Response<UploadPagesFromUriResult> UploadPagesFromUri(
@ -9587,6 +10012,28 @@ namespace Azure { namespace Storage { namespace Blobs {
{
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());
}
auto pHttpResponse = pipeline.Send(request, context);
Azure::Core::Http::RawResponse& httpResponse = *pHttpResponse;
UploadPagesFromUriResult response;
@ -10247,6 +10694,7 @@ namespace Azure { namespace Storage { namespace Blobs {
Azure::Nullable<int32_t> Timeout;
BlobHttpHeaders HttpHeaders;
Storage::Metadata Metadata;
std::map<std::string, std::string> Tags;
Azure::Nullable<std::string> LeaseId;
Azure::Nullable<std::string> EncryptionKey;
Azure::Nullable<std::vector<uint8_t>> EncryptionKeySha256;
@ -10305,6 +10753,20 @@ namespace Azure { namespace Storage { namespace Blobs {
{
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));
}
if (options.LeaseId.HasValue())
{
request.SetHeader("x-ms-lease-id", options.LeaseId.Value());

View File

@ -79,6 +79,7 @@ namespace Azure { namespace Storage { namespace Blobs {
_detail::BlobRestClient::AppendBlob::CreateAppendBlobOptions protocolLayerOptions;
protocolLayerOptions.HttpHeaders = options.HttpHeaders;
protocolLayerOptions.Metadata = options.Metadata;
protocolLayerOptions.Tags = options.Tags;
protocolLayerOptions.LeaseId = options.AccessConditions.LeaseId;
protocolLayerOptions.IfModifiedSince = options.AccessConditions.IfModifiedSince;
protocolLayerOptions.IfUnmodifiedSince = options.AccessConditions.IfUnmodifiedSince;

View File

@ -525,6 +525,8 @@ namespace Azure { namespace Storage { namespace Blobs {
_detail::BlobRestClient::Blob::SetBlobAccessTierOptions protocolLayerOptions;
protocolLayerOptions.AccessTier = tier;
protocolLayerOptions.RehydratePriority = options.RehydratePriority;
protocolLayerOptions.LeaseId = options.AccessConditions.LeaseId;
protocolLayerOptions.IfTags = options.AccessConditions.TagConditions;
return _detail::BlobRestClient::Blob::SetAccessTier(
*m_pipeline, m_blobUrl, protocolLayerOptions, context);
}
@ -536,6 +538,7 @@ namespace Azure { namespace Storage { namespace Blobs {
{
_detail::BlobRestClient::Blob::StartBlobCopyFromUriOptions protocolLayerOptions;
protocolLayerOptions.Metadata = options.Metadata;
protocolLayerOptions.Tags = options.Tags;
protocolLayerOptions.SourceUri = sourceUri;
protocolLayerOptions.AccessTier = options.AccessTier;
protocolLayerOptions.RehydratePriority = options.RehydratePriority;
@ -545,12 +548,12 @@ namespace Azure { namespace Storage { namespace Blobs {
protocolLayerOptions.IfMatch = options.AccessConditions.IfMatch;
protocolLayerOptions.IfNoneMatch = options.AccessConditions.IfNoneMatch;
protocolLayerOptions.IfTags = options.AccessConditions.TagConditions;
protocolLayerOptions.SourceLeaseId = options.SourceAccessConditions.LeaseId;
protocolLayerOptions.SourceIfModifiedSince = options.SourceAccessConditions.IfModifiedSince;
protocolLayerOptions.SourceIfUnmodifiedSince = options.SourceAccessConditions.IfUnmodifiedSince;
protocolLayerOptions.SourceIfMatch = options.SourceAccessConditions.IfMatch;
protocolLayerOptions.SourceIfNoneMatch = options.SourceAccessConditions.IfNoneMatch;
protocolLayerOptions.ShouldSealDestination = options.ShouldSealDestination;
protocolLayerOptions.SourceLeaseId = options.SourceAccessConditions.LeaseId;
protocolLayerOptions.SourceIfTags = options.SourceAccessConditions.TagConditions;
auto response = _detail::BlobRestClient::Blob::StartCopyFromUri(

View File

@ -261,6 +261,10 @@ namespace Azure { namespace Storage { namespace Blobs {
{
i.Details.IsSealed = false;
}
if (i.Details.CopyStatus.HasValue() && !i.Details.IsIncrementalCopy.HasValue())
{
i.Details.IsIncrementalCopy = false;
}
}
ListBlobsPagedResponse pagedResponse;
@ -310,6 +314,10 @@ namespace Azure { namespace Storage { namespace Blobs {
{
i.Details.IsSealed = false;
}
if (i.Details.CopyStatus.HasValue() && !i.Details.IsIncrementalCopy.HasValue())
{
i.Details.IsIncrementalCopy = false;
}
}
ListBlobsByHierarchyPagedResponse pagedResponse;

View File

@ -98,6 +98,7 @@ namespace Azure { namespace Storage { namespace Blobs {
protocolLayerOptions.TransactionalContentHash = options.TransactionalContentHash;
protocolLayerOptions.HttpHeaders = options.HttpHeaders;
protocolLayerOptions.Metadata = options.Metadata;
protocolLayerOptions.Tags = options.Tags;
protocolLayerOptions.AccessTier = options.AccessTier;
protocolLayerOptions.LeaseId = options.AccessConditions.LeaseId;
protocolLayerOptions.IfModifiedSince = options.AccessConditions.IfModifiedSince;
@ -138,6 +139,7 @@ namespace Azure { namespace Storage { namespace Blobs {
UploadBlockBlobOptions uploadBlockBlobOptions;
uploadBlockBlobOptions.HttpHeaders = options.HttpHeaders;
uploadBlockBlobOptions.Metadata = options.Metadata;
uploadBlockBlobOptions.Tags = options.Tags;
uploadBlockBlobOptions.AccessTier = options.AccessTier;
return Upload(contentStream, uploadBlockBlobOptions, context);
}
@ -187,6 +189,7 @@ namespace Azure { namespace Storage { namespace Blobs {
CommitBlockListOptions commitBlockListOptions;
commitBlockListOptions.HttpHeaders = options.HttpHeaders;
commitBlockListOptions.Metadata = options.Metadata;
commitBlockListOptions.Tags = options.Tags;
commitBlockListOptions.AccessTier = options.AccessTier;
auto commitBlockListResponse = CommitBlockList(blockIds, commitBlockListOptions, context);
@ -219,6 +222,7 @@ namespace Azure { namespace Storage { namespace Blobs {
UploadBlockBlobOptions uploadBlockBlobOptions;
uploadBlockBlobOptions.HttpHeaders = options.HttpHeaders;
uploadBlockBlobOptions.Metadata = options.Metadata;
uploadBlockBlobOptions.Tags = options.Tags;
uploadBlockBlobOptions.AccessTier = options.AccessTier;
return Upload(contentStream, uploadBlockBlobOptions, context);
}
@ -276,6 +280,7 @@ namespace Azure { namespace Storage { namespace Blobs {
CommitBlockListOptions commitBlockListOptions;
commitBlockListOptions.HttpHeaders = options.HttpHeaders;
commitBlockListOptions.Metadata = options.Metadata;
commitBlockListOptions.Tags = options.Tags;
commitBlockListOptions.AccessTier = options.AccessTier;
auto commitBlockListResponse = CommitBlockList(blockIds, commitBlockListOptions, context);
@ -351,6 +356,7 @@ namespace Azure { namespace Storage { namespace Blobs {
}
protocolLayerOptions.HttpHeaders = options.HttpHeaders;
protocolLayerOptions.Metadata = options.Metadata;
protocolLayerOptions.Tags = options.Tags;
protocolLayerOptions.AccessTier = options.AccessTier;
protocolLayerOptions.LeaseId = options.AccessConditions.LeaseId;
protocolLayerOptions.IfModifiedSince = options.AccessConditions.IfModifiedSince;

View File

@ -86,6 +86,7 @@ namespace Azure { namespace Storage { namespace Blobs {
protocolLayerOptions.HttpHeaders = options.HttpHeaders;
protocolLayerOptions.Metadata = options.Metadata;
protocolLayerOptions.AccessTier = options.AccessTier;
protocolLayerOptions.Tags = options.Tags;
protocolLayerOptions.LeaseId = options.AccessConditions.LeaseId;
protocolLayerOptions.IfModifiedSince = options.AccessConditions.IfModifiedSince;
protocolLayerOptions.IfUnmodifiedSince = options.AccessConditions.IfUnmodifiedSince;
@ -174,6 +175,10 @@ namespace Azure { namespace Storage { namespace Blobs {
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 (m_customerProvidedKey.HasValue())
{
protocolLayerOptions.EncryptionKey = m_customerProvidedKey.Value().Key;

View File

@ -93,6 +93,19 @@ namespace Azure { namespace Storage { namespace Test {
EXPECT_THROW(appendBlobClient.Delete(), StorageException);
}
TEST_F(AppendBlobClientTest, CreateWithTags)
{
auto appendBlobClient = Azure::Storage::Blobs::AppendBlobClient::CreateFromConnectionString(
StandardStorageConnectionString(), m_containerName, RandomString());
Blobs::CreateAppendBlobOptions options;
options.Tags["key1"] = "value1";
options.Tags["key2"] = "value2";
options.Tags["key3 +-./:=_"] = "v1 +-./:=_";
appendBlobClient.Create(options);
EXPECT_EQ(appendBlobClient.GetTags().Value, options.Tags);
}
TEST_F(AppendBlobClientTest, AccessConditionLastModifiedTime)
{
auto appendBlobClient = Azure::Storage::Blobs::AppendBlobClient::CreateFromConnectionString(
@ -201,31 +214,15 @@ namespace Azure { namespace Storage { namespace Test {
{
auto sourceBlobClient = Azure::Storage::Blobs::AppendBlobClient::CreateFromConnectionString(
StandardStorageConnectionString(), m_containerName, RandomString());
sourceBlobClient.Create();
Blobs::BlobLeaseClient sourceLeaseClient(
sourceBlobClient, Blobs::BlobLeaseClient::CreateUniqueLeaseId());
auto leaseResponse = sourceLeaseClient.Acquire(Blobs::BlobLeaseClient::InfiniteLeaseDuration);
std::string leaseId = leaseResponse.Value.LeaseId;
Azure::ETag eTag = leaseResponse.Value.ETag;
auto lastModifiedTime = leaseResponse.Value.LastModified;
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.LeaseId = Blobs::BlobLeaseClient::CreateUniqueLeaseId();
/*
don't know why, the copy operation also succeeds even if the lease ID doesn't match.
EXPECT_THROW(
destBlobClient.StartCopyFromUri(sourceBlobClient.GetUrl(), options), StorageException);
*/
options.SourceAccessConditions.LeaseId = leaseId;
EXPECT_NO_THROW(destBlobClient.StartCopyFromUri(sourceBlobClient.GetUrl(), options));
}
sourceLeaseClient.Break();
{
Blobs::StartBlobCopyFromUriOptions options;
options.SourceAccessConditions.IfMatch = eTag;
@ -277,20 +274,9 @@ namespace Azure { namespace Storage { namespace Test {
EXPECT_TRUE(getPropertiesResult.Value.IsSealed.HasValue());
EXPECT_FALSE(getPropertiesResult.Value.IsSealed.Value());
Azure::Storage::Blobs::ListBlobsOptions options;
options.Prefix = blobName;
for (auto pageResponse = m_blobContainerClient->ListBlobs(options); pageResponse.HasPage();
pageResponse.MoveToNextPage())
{
for (const auto& blob : pageResponse.Blobs)
{
if (blob.Name == blobName)
{
EXPECT_TRUE(blob.Details.IsSealed.HasValue());
EXPECT_FALSE(blob.Details.IsSealed.Value());
}
}
}
auto blobItem = GetBlobItem(blobName);
EXPECT_TRUE(blobItem.Details.IsSealed.HasValue());
EXPECT_FALSE(blobItem.Details.IsSealed.Value());
Blobs::SealAppendBlobOptions sealOptions;
sealOptions.AccessConditions.IfAppendPositionEqual = m_blobContent.size() + 1;
@ -310,18 +296,9 @@ namespace Azure { namespace Storage { namespace Test {
EXPECT_TRUE(getPropertiesResult.Value.IsSealed.HasValue());
EXPECT_TRUE(getPropertiesResult.Value.IsSealed.Value());
for (auto pageResponse = m_blobContainerClient->ListBlobs(options); pageResponse.HasPage();
pageResponse.MoveToNextPage())
{
for (const auto& blob : pageResponse.Blobs)
{
if (blob.Name == blobName)
{
EXPECT_TRUE(blob.Details.IsSealed.HasValue());
EXPECT_TRUE(blob.Details.IsSealed.Value());
}
}
}
blobItem = GetBlobItem(blobName);
EXPECT_TRUE(blobItem.Details.IsSealed.HasValue());
EXPECT_TRUE(blobItem.Details.IsSealed.Value());
auto blobClient2 = m_blobContainerClient->GetAppendBlobClient(RandomString());

View File

@ -51,6 +51,27 @@ namespace Azure { namespace Storage { namespace Test {
*_internal::ParseConnectionString(StandardStorageConnectionString()).KeyCredential);
}
Blobs::Models::BlobItem BlobContainerClientTest::GetBlobItem(
const std::string& blobName,
Blobs::Models::ListBlobsIncludeFlags include)
{
Blobs::ListBlobsOptions options;
options.Prefix = blobName;
options.Include = include;
for (auto page = m_blobContainerClient->ListBlobs(options); page.HasPage();
page.MoveToNextPage())
{
for (auto& blob : page.Blobs)
{
if (blob.Name == blobName)
{
return std::move(blob);
}
}
}
std::abort();
}
TEST_F(BlobContainerClientTest, CreateDelete)
{
auto container_client = Azure::Storage::Blobs::BlobContainerClient::CreateFromConnectionString(
@ -477,6 +498,23 @@ namespace Azure { namespace Storage { namespace Test {
createOptions.PreventEncryptionScopeOverride.Value());
auto appendBlobClient = containerClient.GetAppendBlobClient(blobName);
auto blobContentInfo = appendBlobClient.Create();
{
Blobs::ListBlobsOptions listOptions;
listOptions.Prefix = blobName;
for (auto page = containerClient.ListBlobs(listOptions); page.HasPage();
page.MoveToNextPage())
{
for (auto& blob : page.Blobs)
{
if (blob.Name == blobName)
{
EXPECT_TRUE(blob.Details.IsServerEncrypted);
EXPECT_TRUE(blob.Details.EncryptionScope.HasValue());
EXPECT_EQ(blob.Details.EncryptionScope.Value(), TestEncryptionScope);
}
}
}
}
appendBlobClient.Delete();
EXPECT_TRUE(blobContentInfo.Value.EncryptionScope.HasValue());
EXPECT_EQ(blobContentInfo.Value.EncryptionScope.Value(), TestEncryptionScope);
@ -498,6 +536,10 @@ namespace Azure { namespace Storage { namespace Test {
auto blobContentInfo = appendBlobClient.Create();
EXPECT_TRUE(blobContentInfo.Value.EncryptionScope.HasValue());
EXPECT_EQ(blobContentInfo.Value.EncryptionScope.Value(), TestEncryptionScope);
auto setMetadataRes = appendBlobClient.SetMetadata({});
EXPECT_TRUE(setMetadataRes.Value.IsServerEncrypted);
ASSERT_TRUE(setMetadataRes.Value.EncryptionScope.HasValue());
EXPECT_EQ(setMetadataRes.Value.EncryptionScope.Value(), TestEncryptionScope);
auto properties = appendBlobClient.GetProperties().Value;
EXPECT_TRUE(properties.EncryptionScope.HasValue());
EXPECT_EQ(properties.EncryptionScope.Value(), TestEncryptionScope);
@ -569,13 +611,24 @@ namespace Azure { namespace Storage { namespace Test {
EXPECT_TRUE(blobContentInfo.EncryptionKeySha256.HasValue());
EXPECT_EQ(
blobContentInfo.EncryptionKeySha256.Value(), options.CustomerProvidedKey.Value().KeyHash);
auto blobItem = GetBlobItem(appendBlobName);
EXPECT_TRUE(blobItem.Details.IsServerEncrypted);
EXPECT_TRUE(blobItem.Details.EncryptionKeySha256.HasValue());
EXPECT_EQ(
blobItem.Details.EncryptionKeySha256.Value(),
options.CustomerProvidedKey.Value().KeyHash);
bodyStream.Rewind();
EXPECT_NO_THROW(appendBlob.AppendBlock(bodyStream));
EXPECT_NO_THROW(appendBlob.AppendBlockFromUri(copySourceBlob.GetUrl() + GetSas()));
EXPECT_NO_THROW(appendBlob.Download());
EXPECT_NO_THROW(appendBlob.GetProperties());
EXPECT_NO_THROW(appendBlob.SetMetadata({}));
auto setMetadataRes = appendBlob.SetMetadata({});
EXPECT_TRUE(setMetadataRes.Value.IsServerEncrypted);
ASSERT_TRUE(setMetadataRes.Value.EncryptionKeySha256.HasValue());
EXPECT_EQ(
setMetadataRes.Value.EncryptionKeySha256.Value(),
options.CustomerProvidedKey.Value().KeyHash);
EXPECT_NO_THROW(appendBlob.CreateSnapshot());
auto appendBlobClientWithoutEncryptionKey
@ -681,16 +734,47 @@ namespace Azure { namespace Storage { namespace Test {
StandardStorageConnectionString(), LowercaseRandomString());
containerClient.Create();
std::string leaseId = Blobs::BlobLeaseClient::CreateUniqueLeaseId();
const std::string leaseId = Blobs::BlobLeaseClient::CreateUniqueLeaseId();
const std::string dummyLeaseId = Blobs::BlobLeaseClient::CreateUniqueLeaseId();
Blobs::BlobLeaseClient leaseClient(containerClient, leaseId);
leaseClient.Acquire(std::chrono::seconds(30));
EXPECT_THROW(containerClient.Delete(), StorageException);
Blobs::DeleteBlobContainerOptions options;
options.AccessConditions.LeaseId = leaseId;
EXPECT_NO_THROW(containerClient.Delete(options));
{
Blobs::GetBlobContainerPropertiesOptions options;
options.AccessConditions.LeaseId = dummyLeaseId;
EXPECT_THROW(containerClient.GetProperties(options), StorageException);
options.AccessConditions.LeaseId = leaseId;
EXPECT_NO_THROW(containerClient.GetProperties(options));
}
{
Blobs::SetBlobContainerMetadataOptions options;
options.AccessConditions.LeaseId = dummyLeaseId;
EXPECT_THROW(containerClient.SetMetadata({}, options), StorageException);
options.AccessConditions.LeaseId = leaseId;
EXPECT_NO_THROW(containerClient.SetMetadata({}, options));
}
{
Blobs::GetBlobContainerAccessPolicyOptions options;
options.AccessConditions.LeaseId = dummyLeaseId;
EXPECT_THROW(containerClient.GetAccessPolicy(options), StorageException);
options.AccessConditions.LeaseId = leaseId;
EXPECT_NO_THROW(containerClient.GetAccessPolicy(options));
}
{
Blobs::SetBlobContainerAccessPolicyOptions options;
options.AccessConditions.LeaseId = dummyLeaseId;
EXPECT_THROW(containerClient.SetAccessPolicy(options), StorageException);
options.AccessConditions.LeaseId = leaseId;
EXPECT_NO_THROW(containerClient.SetAccessPolicy(options));
}
{
EXPECT_THROW(containerClient.Delete(), StorageException);
Blobs::DeleteBlobContainerOptions options;
options.AccessConditions.LeaseId = leaseId;
EXPECT_NO_THROW(containerClient.Delete(options));
}
}
TEST_F(BlobContainerClientTest, DISABLED_Tags)
TEST_F(BlobContainerClientTest, Tags)
{
std::string blobName = RandomString();
auto blobClient = Azure::Storage::Blobs::AppendBlobClient::CreateFromConnectionString(
@ -710,6 +794,8 @@ namespace Azure { namespace Storage { namespace Test {
std::string v2 = RandomString();
std::string c3 = "k" + RandomString();
std::string v3 = RandomString();
std::string c4 = "key3 +-./:=_";
std::string v4 = "v1 +-./:=_";
tags[c1] = v1;
tags[c2] = v2;
tags[c3] = v3;
@ -722,11 +808,16 @@ namespace Azure { namespace Storage { namespace Test {
properties = blobClient.GetProperties().Value;
EXPECT_TRUE(properties.TagCount.HasValue());
EXPECT_EQ(properties.TagCount.Value(), static_cast<int64_t>(tags.size()));
EXPECT_EQ(properties.TagCount.Value(), static_cast<int32_t>(tags.size()));
downloadRet = blobClient.Download();
EXPECT_TRUE(downloadRet.Value.Details.TagCount.HasValue());
EXPECT_EQ(downloadRet.Value.Details.TagCount.Value(), static_cast<int64_t>(tags.size()));
ASSERT_TRUE(downloadRet.Value.Details.TagCount.HasValue());
EXPECT_EQ(downloadRet.Value.Details.TagCount.Value(), static_cast<int32_t>(tags.size()));
auto blobItem = GetBlobItem(blobName, Blobs::Models::ListBlobsIncludeFlags::Tags);
ASSERT_TRUE(blobItem.Details.TagCount.HasValue());
EXPECT_EQ(blobItem.Details.TagCount.Value(), static_cast<int32_t>(tags.size()));
EXPECT_EQ(blobItem.Details.Tags, tags);
auto blobServiceClient = Azure::Storage::Blobs::BlobServiceClient::CreateFromConnectionString(
StandardStorageConnectionString());
@ -762,7 +853,7 @@ namespace Azure { namespace Storage { namespace Test {
EXPECT_EQ(findResults[0].BlobContainerName, m_containerName);
}
TEST_F(BlobContainerClientTest, DISABLED_AccessConditionTags)
TEST_F(BlobContainerClientTest, AccessConditionTags)
{
std::map<std::string, std::string> tags;
std::string c1 = "k" + RandomString();
@ -960,6 +1051,15 @@ namespace Azure { namespace Storage { namespace Test {
blockBlobClient.UploadFrom(contentData.data(), contentData.size());
blockBlobClient.SetTags(tags);
{
Blobs::SetBlobAccessTierOptions options;
options.AccessConditions.TagConditions = successWhereExpression;
EXPECT_NO_THROW(blockBlobClient.SetAccessTier(Blobs::Models::AccessTier::Hot, options));
options.AccessConditions.TagConditions = failWhereExpression;
EXPECT_THROW(
blockBlobClient.SetAccessTier(Blobs::Models::AccessTier::Hot, options), StorageException);
}
{
Blobs::UploadBlockBlobOptions options;
options.AccessConditions.TagConditions = failWhereExpression;

View File

@ -13,6 +13,9 @@ namespace Azure { namespace Storage { namespace Test {
static void TearDownTestSuite();
static std::string GetSas();
static Blobs::Models::BlobItem GetBlobItem(
const std::string& blobName,
Blobs::Models::ListBlobsIncludeFlags include = Blobs::Models::ListBlobsIncludeFlags::None);
static std::shared_ptr<Azure::Storage::Blobs::BlobContainerClient> m_blobContainerClient;
static std::string m_containerName;

View File

@ -105,19 +105,19 @@ namespace Azure { namespace Storage { namespace Test {
blobClient0.Delete(options);
};
// auto verify_blob_tags = [&](const std::string& sas) {
// blobClient0.Create();
// std::map<std::string, std::string> tags = {{"tag_key1", "tag_value1"}};
// blobClient0.SetTags(tags);
// auto blobClient = Blobs::AppendBlobClient(blobUrl + sas);
// EXPECT_NO_THROW(blobClient.GetTags());
// blobClient0.Delete();
//};
auto verify_blob_tags = [&](const std::string& sas) {
blobClient0.Create();
std::map<std::string, std::string> tags = {{"tag_key1", "tag_value1"}};
blobClient0.SetTags(tags);
auto blobClient = Blobs::AppendBlobClient(blobUrl + sas);
EXPECT_NO_THROW(blobClient.GetTags());
blobClient0.Delete();
};
// auto verify_blob_filter = [&](const std::string& sas) {
// auto serviceClient = Blobs::BlobServiceClient(serviceUrl + sas);
// EXPECT_NO_THROW(serviceClient.FindBlobsByTags("\"tag_key1\" = 'tag_value1'"));
//};
auto verify_blob_filter = [&](const std::string& sas) {
auto serviceClient = Blobs::BlobServiceClient(serviceUrl + sas);
EXPECT_NO_THROW(serviceClient.FindBlobsByTags("\"tag_key1\" = 'tag_value1'"));
};
for (auto permissions : {
Sas::AccountSasPermissions::All,
@ -161,11 +161,11 @@ namespace Azure { namespace Storage { namespace Test {
}
if ((permissions & Sas::AccountSasPermissions::Tags) == Sas::AccountSasPermissions::Tags)
{
// verify_blob_tags(sasToken);
verify_blob_tags(sasToken);
}
if ((permissions & Sas::AccountSasPermissions::Filter) == Sas::AccountSasPermissions::Filter)
{
// verify_blob_filter(sasToken);
verify_blob_filter(sasToken);
}
}
@ -210,8 +210,8 @@ namespace Azure { namespace Storage { namespace Test {
}
if ((permissions & Sas::BlobSasPermissions::Tags) == Sas::BlobSasPermissions::Tags)
{
// verify_blob_tags(sasToken);
// verify_blob_tags(sasToken2);
verify_blob_tags(sasToken);
verify_blob_tags(sasToken2);
}
}
@ -322,8 +322,8 @@ namespace Azure { namespace Storage { namespace Test {
if ((permissions & Sas::BlobContainerSasPermissions::Tags)
== Sas::BlobContainerSasPermissions::Tags)
{
// verify_blob_tags(sasToken);
// verify_blob_tags(sasToken2);
verify_blob_tags(sasToken);
verify_blob_tags(sasToken2);
}
}

View File

@ -77,6 +77,32 @@ namespace Azure { namespace Storage { namespace Test {
EXPECT_THROW(blockBlobClient.Delete(), StorageException);
}
TEST_F(BlockBlobClientTest, SoftDelete)
{
const std::string blobName = RandomString();
std::vector<uint8_t> emptyContent;
auto blockBlobClient = Azure::Storage::Blobs::BlockBlobClient::CreateFromConnectionString(
StandardStorageConnectionString(), m_containerName, blobName);
auto blobContent = Azure::Core::IO::MemoryBodyStream(emptyContent.data(), emptyContent.size());
blockBlobClient.Upload(blobContent);
auto blobItem = GetBlobItem(blobName);
EXPECT_FALSE(blobItem.IsDeleted);
EXPECT_FALSE(blobItem.Details.DeletedOn.HasValue());
EXPECT_FALSE(blobItem.Details.RemainingRetentionDays.HasValue());
blockBlobClient.Delete();
/*
// Soft delete doesn't work in storage account with versioning enabled.
blobItem = GetBlobItem(blobName, Blobs::Models::ListBlobsIncludeFlags::Deleted);
EXPECT_TRUE(blobItem.IsDeleted);
ASSERT_TRUE(blobItem.Details.DeletedOn.HasValue());
EXPECT_TRUE(IsValidTime(blobItem.Details.DeletedOn.Value()));
EXPECT_TRUE(blobItem.Details.RemainingRetentionDays.HasValue());
*/
}
TEST_F(BlockBlobClientTest, UploadDownload)
{
auto res = m_blockBlobClient->Download();
@ -108,6 +134,49 @@ namespace Azure { namespace Storage { namespace Test {
EXPECT_EQ(res.Value.BlobSize, static_cast<int64_t>(m_blobContent.size()));
}
TEST_F(BlockBlobClientTest, UploadWithTags)
{
auto blockBlobClient = Azure::Storage::Blobs::BlockBlobClient::CreateFromConnectionString(
StandardStorageConnectionString(), m_containerName, RandomString());
std::map<std::string, std::string> tags;
tags["key1"] = "value1";
tags["key2"] = "value2";
tags["key3 +-./:=_"] = "v1 +-./:=_";
std::vector<uint8_t> blobContent(100, 'a');
{
Blobs::UploadBlockBlobOptions options;
options.Tags = tags;
auto stream = Azure::Core::IO::MemoryBodyStream(blobContent.data(), blobContent.size());
blockBlobClient.Upload(stream, options);
EXPECT_EQ(blockBlobClient.GetTags().Value, tags);
blockBlobClient.Delete();
}
{
Blobs::UploadBlockBlobFromOptions options;
options.TransferOptions.SingleUploadThreshold = 0;
options.TransferOptions.ChunkSize = blobContent.size() / 2;
options.Tags = tags;
{
blockBlobClient.UploadFrom(blobContent.data(), blobContent.size(), options);
EXPECT_EQ(blockBlobClient.GetTags().Value, tags);
blockBlobClient.Delete();
}
{
const std::string tempFilename = RandomString();
{
Azure::Storage::_internal::FileWriter fileWriter(tempFilename);
fileWriter.Write(blobContent.data(), blobContent.size(), 0);
}
blockBlobClient.UploadFrom(tempFilename, options);
EXPECT_EQ(blockBlobClient.GetTags().Value, tags);
blockBlobClient.Delete();
}
}
}
TEST_F(BlockBlobClientTest, DownloadTransactionalHash)
{
const std::vector<uint8_t> dataPart1(static_cast<size_t>(4_MB + 1), 'a');
@ -204,24 +273,7 @@ namespace Azure { namespace Storage { namespace Test {
EXPECT_TRUE(IsValidTime(res.Value.LastAccessedOn.Value()));
}
{
Azure::DateTime lastAccessedOn;
Azure::Storage::Blobs::ListBlobsOptions options;
options.Prefix = m_blobName;
for (auto pageResult = m_blobContainerClient->ListBlobs(options); pageResult.HasPage();
pageResult.MoveToNextPage())
{
for (const auto& blob : pageResult.Blobs)
{
if (blob.Name == m_blobName)
{
lastAccessedOn = blob.Details.LastAccessedOn.Value();
break;
}
}
}
EXPECT_TRUE(IsValidTime(lastAccessedOn));
EXPECT_TRUE(IsValidTime(GetBlobItem(m_blobName).Details.LastAccessedOn.Value()));
}
}
@ -256,36 +308,52 @@ namespace Azure { namespace Storage { namespace Test {
TEST_F(BlockBlobClientTest, CopyFromUri)
{
auto blobClient = m_blobContainerClient->GetBlobClient(RandomString());
const std::string blobName = RandomString();
auto blobClient = m_blobContainerClient->GetBlobClient(blobName);
auto res = blobClient.StartCopyFromUri(m_blockBlobClient->GetUrl());
EXPECT_EQ(res.GetRawResponse().GetStatusCode(), Azure::Core::Http::HttpStatusCode::Accepted);
res.PollUntilDone(std::chrono::seconds(1));
auto properties = blobClient.GetProperties().Value;
EXPECT_FALSE(properties.CopyId.Value().empty());
EXPECT_FALSE(properties.CopySource.Value().empty());
EXPECT_TRUE(
properties.CopyStatus.Value() == Azure::Storage::Blobs::Models::CopyStatus::Pending
|| properties.CopyStatus.Value() == Azure::Storage::Blobs::Models::CopyStatus::Success);
properties.CopyStatus.Value() == Azure::Storage::Blobs::Models::CopyStatus::Success);
EXPECT_FALSE(properties.CopyProgress.Value().empty());
if (properties.CopyStatus.Value() == Azure::Storage::Blobs::Models::CopyStatus::Success)
{
EXPECT_TRUE(IsValidTime(properties.CopyCompletedOn.Value()));
}
EXPECT_TRUE(IsValidTime(properties.CopyCompletedOn.Value()));
ASSERT_TRUE(properties.IsIncrementalCopy.HasValue());
EXPECT_FALSE(properties.IsIncrementalCopy.Value());
EXPECT_FALSE(properties.IncrementalCopyDestinationSnapshot.HasValue());
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::Pending
|| downloadResult.Value.Details.CopyStatus.Value()
== Azure::Storage::Blobs::Models::CopyStatus::Success);
== Azure::Storage::Blobs::Models::CopyStatus::Success);
EXPECT_FALSE(downloadResult.Value.Details.CopyProgress.Value().empty());
if (downloadResult.Value.Details.CopyStatus.Value()
== Azure::Storage::Blobs::Models::CopyStatus::Success)
{
EXPECT_TRUE(IsValidTime(downloadResult.Value.Details.CopyCompletedOn.Value()));
}
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, CopyWithTags)
{
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);
EXPECT_EQ(blobClient.GetTags().Value, options.Tags);
}
TEST_F(BlockBlobClientTest, SnapShotVersions)
@ -373,28 +441,16 @@ namespace Azure { namespace Storage { namespace Test {
EXPECT_FALSE(downloadResponse.Value.Details.IsCurrentVersion.Value());
EXPECT_EQ(version1, downloadResponse.Value.Details.VersionId.Value());
Azure::Storage::Blobs::ListBlobsOptions options;
options.Prefix = blobName;
options.Include = Blobs::Models::ListBlobsIncludeFlags::Versions;
for (auto pageResult = m_blobContainerClient->ListBlobs(options); pageResult.HasPage();
pageResult.MoveToNextPage())
auto blobItem = GetBlobItem(blobName, Blobs::Models::ListBlobsIncludeFlags::Versions);
ASSERT_TRUE(blobItem.VersionId.HasValue());
ASSERT_TRUE(blobItem.IsCurrentVersion.HasValue());
if (blobItem.VersionId.Value() == latestVersion)
{
for (const auto& blob : pageResult.Blobs)
{
if (blob.Name == blobName)
{
ASSERT_TRUE(blob.VersionId.HasValue());
ASSERT_TRUE(blob.IsCurrentVersion.HasValue());
if (blob.VersionId.Value() == latestVersion)
{
EXPECT_TRUE(blob.IsCurrentVersion.Value());
}
else
{
EXPECT_FALSE(blob.IsCurrentVersion.Value());
}
}
}
EXPECT_TRUE(blobItem.IsCurrentVersion.Value());
}
else
{
EXPECT_FALSE(blobItem.IsCurrentVersion.Value());
}
}
@ -1023,21 +1079,11 @@ namespace Azure { namespace Storage { namespace Test {
EXPECT_TRUE(properties.IsAccessTierInferred.Value());
EXPECT_FALSE(properties.AccessTierChangedOn.HasValue());
Azure::Storage::Blobs::ListBlobsOptions options;
options.Prefix = blobName;
for (auto pageResult = m_blobContainerClient->ListBlobs(options); pageResult.HasPage();
pageResult.MoveToNextPage())
{
for (const auto& blob : pageResult.Blobs)
{
if (blob.Name == blobName)
{
ASSERT_TRUE(blob.Details.AccessTier.HasValue());
ASSERT_TRUE(blob.Details.IsAccessTierInferred.HasValue());
EXPECT_TRUE(blob.Details.IsAccessTierInferred.Value());
}
}
}
auto blobItem = GetBlobItem(blobName);
ASSERT_TRUE(blobItem.Details.AccessTier.HasValue());
ASSERT_TRUE(blobItem.Details.IsAccessTierInferred.HasValue());
EXPECT_TRUE(blobItem.Details.IsAccessTierInferred.Value());
EXPECT_FALSE(blobItem.Details.AccessTierChangedOn.HasValue());
// choose a different tier
auto targetTier = properties.AccessTier.Value() == Blobs::Models::AccessTier::Hot
@ -1051,19 +1097,48 @@ namespace Azure { namespace Storage { namespace Test {
EXPECT_FALSE(properties.IsAccessTierInferred.Value());
EXPECT_TRUE(properties.AccessTierChangedOn.HasValue());
for (auto pageResult = m_blobContainerClient->ListBlobs(options); pageResult.HasPage();
pageResult.MoveToNextPage())
{
for (const auto& blob : pageResult.Blobs)
{
if (blob.Name == blobName)
{
ASSERT_TRUE(blob.Details.AccessTier.HasValue());
ASSERT_TRUE(blob.Details.IsAccessTierInferred.HasValue());
EXPECT_FALSE(blob.Details.IsAccessTierInferred.Value());
}
}
}
blobItem = GetBlobItem(blobName);
ASSERT_TRUE(blobItem.Details.AccessTier.HasValue());
ASSERT_TRUE(blobItem.Details.IsAccessTierInferred.HasValue());
EXPECT_FALSE(blobItem.Details.IsAccessTierInferred.Value());
EXPECT_TRUE(blobItem.Details.AccessTierChangedOn.HasValue());
// set to archive, then rehydrate
blobClient.SetAccessTier(Blobs::Models::AccessTier::Archive);
blobClient.SetAccessTier(Blobs::Models::AccessTier::Hot);
properties = blobClient.GetProperties().Value;
ASSERT_TRUE(properties.ArchiveStatus.HasValue());
EXPECT_EQ(
properties.ArchiveStatus.Value(), Blobs::Models::ArchiveStatus::RehydratePendingToHot);
ASSERT_TRUE(properties.RehydratePriority.HasValue());
EXPECT_EQ(properties.RehydratePriority.Value(), Blobs::Models::RehydratePriority::Standard);
blobItem = GetBlobItem(blobName);
ASSERT_TRUE(blobItem.Details.ArchiveStatus.HasValue());
EXPECT_EQ(
blobItem.Details.ArchiveStatus.Value(),
Blobs::Models::ArchiveStatus::RehydratePendingToHot);
ASSERT_TRUE(blobItem.Details.RehydratePriority.HasValue());
EXPECT_EQ(
blobItem.Details.RehydratePriority.Value(), Blobs::Models::RehydratePriority::Standard);
}
TEST_F(BlockBlobClientTest, SetTierWithLeaseId)
{
std::vector<uint8_t> emptyContent;
auto blobClient = Azure::Storage::Blobs::BlockBlobClient::CreateFromConnectionString(
StandardStorageConnectionString(), m_containerName, RandomString());
blobClient.UploadFrom(emptyContent.data(), emptyContent.size());
const std::string leaseId = Blobs::BlobLeaseClient::CreateUniqueLeaseId();
Blobs::BlobLeaseClient leaseClient(blobClient, leaseId);
leaseClient.Acquire(std::chrono::seconds(30));
EXPECT_THROW(blobClient.SetAccessTier(Blobs::Models::AccessTier::Cool), StorageException);
Blobs::SetBlobAccessTierOptions options;
options.AccessConditions.LeaseId = leaseId;
EXPECT_NO_THROW(blobClient.SetAccessTier(Blobs::Models::AccessTier::Cool, options));
}
}}} // namespace Azure::Storage::Test

View File

@ -62,6 +62,19 @@ namespace Azure { namespace Storage { namespace Test {
EXPECT_THROW(pageBlobClient.Delete(), StorageException);
}
TEST_F(PageBlobClientTest, CreateWithTags)
{
auto pageBlobClient = Azure::Storage::Blobs::PageBlobClient::CreateFromConnectionString(
StandardStorageConnectionString(), m_containerName, RandomString());
Blobs::CreatePageBlobOptions options;
options.Tags["key1"] = "value1";
options.Tags["key2"] = "value2";
options.Tags["key3 +-./:=_"] = "v1 +-./:=_";
pageBlobClient.Create(512, options);
EXPECT_EQ(pageBlobClient.GetTags().Value, options.Tags);
}
TEST_F(PageBlobClientTest, Resize)
{
auto pageBlobClient = Azure::Storage::Blobs::PageBlobClient::CreateFromConnectionString(
@ -162,8 +175,9 @@ namespace Azure { namespace Storage { namespace Test {
TEST_F(PageBlobClientTest, StartCopyIncremental)
{
const std::string blobName = RandomString();
auto pageBlobClient = Azure::Storage::Blobs::PageBlobClient::CreateFromConnectionString(
StandardStorageConnectionString(), m_containerName, RandomString());
StandardStorageConnectionString(), m_containerName, blobName);
std::string snapshot = m_pageBlobClient->CreateSnapshot().Value.Snapshot;
Azure::Core::Url sourceUri(m_pageBlobClient->WithSnapshot(snapshot).GetUrl());
auto copyInfo = pageBlobClient.StartCopyIncremental(AppendQueryParameters(sourceUri, GetSas()));
@ -184,6 +198,12 @@ namespace Azure { namespace Storage { namespace Test {
EXPECT_TRUE(IsValidTime(getPropertiesResult.Value.CopyCompletedOn.Value()));
ASSERT_TRUE(getPropertiesResult.Value.CopyProgress.HasValue());
EXPECT_FALSE(getPropertiesResult.Value.CopyProgress.Value().empty());
auto blobItem = GetBlobItem(blobName, Blobs::Models::ListBlobsIncludeFlags::Copy);
ASSERT_TRUE(blobItem.Details.IsIncrementalCopy.HasValue());
EXPECT_TRUE(blobItem.Details.IsIncrementalCopy.Value());
ASSERT_TRUE(blobItem.Details.IncrementalCopyDestinationSnapshot.HasValue());
EXPECT_FALSE(blobItem.Details.IncrementalCopyDestinationSnapshot.Value().empty());
}
TEST_F(PageBlobClientTest, Lease)
@ -329,4 +349,56 @@ namespace Azure { namespace Storage { namespace Test {
EXPECT_EQ(downloadStream->ReadToEnd(Azure::Core::Context()), m_blobContent);
}
TEST_F(PageBlobClientTest, SourceBlobAccessConditions)
{
auto sourceBlobClient = Azure::Storage::Blobs::PageBlobClient::CreateFromConnectionString(
StandardStorageConnectionString(), m_containerName, RandomString());
const std::string url = sourceBlobClient.GetUrl() + GetSas();
const int64_t blobSize = 512;
auto createResponse = sourceBlobClient.Create(blobSize);
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::PageBlobClient::CreateFromConnectionString(
StandardStorageConnectionString(), m_containerName, RandomString());
destBlobClient.Create(blobSize);
{
Blobs::UploadPagesFromUriOptions options;
options.SourceAccessConditions.IfMatch = eTag;
EXPECT_NO_THROW(destBlobClient.UploadPagesFromUri(0, url, {0, blobSize}, options));
options.SourceAccessConditions.IfMatch = DummyETag;
EXPECT_THROW(
destBlobClient.UploadPagesFromUri(0, url, {0, blobSize}, options), StorageException);
}
{
Blobs::UploadPagesFromUriOptions options;
options.SourceAccessConditions.IfNoneMatch = DummyETag;
EXPECT_NO_THROW(destBlobClient.UploadPagesFromUri(0, url, {0, blobSize}, options));
options.SourceAccessConditions.IfNoneMatch = eTag;
EXPECT_THROW(
destBlobClient.UploadPagesFromUri(0, url, {0, blobSize}, options), StorageException);
}
{
Blobs::UploadPagesFromUriOptions options;
options.SourceAccessConditions.IfModifiedSince = timeBeforeStr;
EXPECT_NO_THROW(destBlobClient.UploadPagesFromUri(0, url, {0, blobSize}, options));
options.SourceAccessConditions.IfModifiedSince = timeAfterStr;
EXPECT_THROW(
destBlobClient.UploadPagesFromUri(0, url, {0, blobSize}, options), StorageException);
}
{
Blobs::UploadPagesFromUriOptions options;
options.SourceAccessConditions.IfUnmodifiedSince = timeAfterStr;
EXPECT_NO_THROW(destBlobClient.UploadPagesFromUri(0, url, {0, blobSize}, options));
options.SourceAccessConditions.IfUnmodifiedSince = timeBeforeStr;
EXPECT_THROW(
destBlobClient.UploadPagesFromUri(0, url, {0, blobSize}, options), StorageException);
}
}
}}} // namespace Azure::Storage::Test

View File

@ -12,6 +12,10 @@
## 12.0.1 (2021-07-07)
### New Features
- Added `ETag` and `LastModified` into `ScheduleFileDeletionResult`.
### Bug Fixes
- Fixed a bug where transactional MD5 hash was treated as blob MD5 hash when downloading partial blob.

View File

@ -333,9 +333,11 @@ namespace Azure { namespace Storage { namespace Test {
{
{
auto client = m_fileSystemClient->GetFileClient(RandomString());
EXPECT_NO_THROW(client.Create());
EXPECT_NO_THROW(
client.ScheduleDeletion(Files::DataLake::ScheduleFileExpiryOriginType::NeverExpire));
auto createResponse = client.Create();
auto scheduleDeletionResponse
= client.ScheduleDeletion(Files::DataLake::ScheduleFileExpiryOriginType::NeverExpire);
EXPECT_EQ(scheduleDeletionResponse.Value.ETag, createResponse.Value.ETag);
EXPECT_EQ(scheduleDeletionResponse.Value.LastModified, createResponse.Value.LastModified);
}
{
auto client = m_fileSystemClient->GetFileClient(RandomString());