Storage STG99 Features (#6622)

* Changed QueueProperties.ApproximateMessagesCount from int to long (#6584)

* Storage/STG99 Added ShareSnapshotNotFound in ShareErrorCode (#6612)

* Share SnapshotNotFound error code support

* add test record

* Storage/STG99 Made error message for invalid x-ms-version more user-friendly  (#6613)

* add invalid version erorr message support

* Update find logic

* Stg99/Add test records and update change logs (#6620)

* Update test records and change logs

* update change log
This commit is contained in:
microzchang 2025-06-23 11:31:03 +08:00 committed by GitHub
parent d9eacda722
commit 17563c7c01
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 151 additions and 47 deletions

View File

@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets", "AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "cpp", "AssetsRepoPrefixPath": "cpp",
"TagPrefix": "cpp/storage", "TagPrefix": "cpp/storage",
"Tag": "cpp/storage_7bea1dff90" "Tag": "cpp/storage_7f9ca72801"
} }

View File

@ -1,14 +1,10 @@
# Release History # Release History
## 12.14.0-beta.2 (Unreleased) ## 12.15.0-beta.1 (2025-06-11)
### Features Added ### Features Added
### Breaking Changes - Added more useful error message when the SDK encounters an x-ms-version mis-match issue.
### Bugs Fixed
### Other Changes
## 12.14.0-beta.1 (2025-05-13) ## 12.14.0-beta.1 (2025-05-13)

View File

@ -2264,4 +2264,20 @@ namespace Azure { namespace Storage { namespace Test {
blobProperties = versionClient.GetProperties().Value; blobProperties = versionClient.GetProperties().Value;
EXPECT_TRUE(blobProperties.HasLegalHold); EXPECT_TRUE(blobProperties.HasLegalHold);
} }
TEST_F(BlockBlobClientTest, InvalidVersionMessage)
{
Blobs::BlobClientOptions options;
options.ApiVersion = "3015-11-11";
auto blobClient = GetBlockBlobClientForTest(LowercaseRandomString(), options);
try
{
blobClient.Download();
}
catch (const StorageException& e)
{
EXPECT_EQ(e.ErrorCode, _internal::InvalidHeaderValueErrorCode);
EXPECT_EQ(e.Message, _internal::InvalidVersionHeaderMessage);
}
}
}}} // namespace Azure::Storage::Test }}} // namespace Azure::Storage::Test

View File

@ -1,12 +1,10 @@
# Release History # Release History
## 12.11.0-beta.1 (Unreleased) ## 12.12.0-beta.1 (2025-06-11)
### Features Added ### Features Added
### Breaking Changes - Added more useful error message when the SDK encounters an x-ms-version mis-match issue.
### Bugs Fixed
### Other Changes ### Other Changes

View File

@ -21,6 +21,11 @@ namespace Azure { namespace Storage { namespace _internal {
constexpr static const char* HttpHeaderContentType = "content-type"; constexpr static const char* HttpHeaderContentType = "content-type";
constexpr static const char* HttpHeaderContentLength = "content-length"; constexpr static const char* HttpHeaderContentLength = "content-length";
constexpr static const char* HttpHeaderContentRange = "content-range"; constexpr static const char* HttpHeaderContentRange = "content-range";
constexpr static const char* InvalidHeaderValueErrorCode = "InvalidHeaderValue";
constexpr static const char* InvalidVersionHeaderMessage
= "The provided service version is not enabled on this storage account. Please see "
"https://learn.microsoft.com/rest/api/storageservices/"
"versioning-for-the-azure-storage-services for additional information.";
constexpr int ReliableStreamRetryCount = 3; constexpr int ReliableStreamRetryCount = 3;
}}} // namespace Azure::Storage::_internal }}} // namespace Azure::Storage::_internal

View File

@ -147,6 +147,14 @@ namespace Azure { namespace Storage {
errorCode = response->GetHeaders().at("x-ms-error-code"); errorCode = response->GetHeaders().at("x-ms-error-code");
} }
// Optimize error messages
const auto headerName = additionalInformation.find("HeaderName");
if (errorCode == _internal::InvalidHeaderValueErrorCode
&& headerName != additionalInformation.end() && headerName->second == "x-ms-version")
{
message = _internal::InvalidVersionHeaderMessage;
}
StorageException result = StorageException( StorageException result = StorageException(
std::to_string(static_cast<std::underlying_type<Azure::Core::Http::HttpStatusCode>::type>( std::to_string(static_cast<std::underlying_type<Azure::Core::Http::HttpStatusCode>::type>(
httpStatusCode)) httpStatusCode))

View File

@ -1,14 +1,10 @@
# Release History # Release History
## 12.13.0-beta.1 (Unreleased) ## 12.13.0-beta.1 (2025-06-11)
### Features Added ### Features Added
### Breaking Changes - Added more useful error message when the SDK encounters an x-ms-version mis-match issue.
### Bugs Fixed
### Other Changes
## 12.12.0 (2024-09-17) ## 12.12.0 (2024-09-17)

View File

@ -1,14 +1,11 @@
# Release History # Release History
## 12.14.0-beta.2 (Unreleased) ## 12.15.0-beta.1 (2025-06-11)
### Features Added ### Features Added
### Breaking Changes - `ShareClient::DeleteIfExists()` will return `false` when error code is `ShareSnapshotNotFound`.
- Added more useful error message when the SDK encounters an x-ms-version mis-match issue.
### Bugs Fixed
### Other Changes
## 12.14.0-beta.1 (2025-05-13) ## 12.14.0-beta.1 (2025-05-13)

View File

@ -20,6 +20,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
constexpr static const char* ResourceNotFound = "ResourceNotFound"; constexpr static const char* ResourceNotFound = "ResourceNotFound";
constexpr static const char* ShareAlreadyExists = "ShareAlreadyExists"; constexpr static const char* ShareAlreadyExists = "ShareAlreadyExists";
constexpr static const char* ShareNotFound = "ShareNotFound"; constexpr static const char* ShareNotFound = "ShareNotFound";
constexpr static const char* ShareSnapshotNotFound = "ShareSnapshotNotFound";
constexpr static const char* ResourceAlreadyExists = "ResourceAlreadyExists"; constexpr static const char* ResourceAlreadyExists = "ResourceAlreadyExists";
} // namespace _detail } // namespace _detail

View File

@ -222,7 +222,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
} }
catch (StorageException& e) catch (StorageException& e)
{ {
if (e.ErrorCode == _detail::ShareNotFound) if (e.ErrorCode == _detail::ShareNotFound || e.ErrorCode == _detail::ShareSnapshotNotFound)
{ {
Models::DeleteShareResult ret; Models::DeleteShareResult ret;
ret.Deleted = false; ret.Deleted = false;

View File

@ -124,6 +124,15 @@ namespace Azure { namespace Storage { namespace Test {
} }
} }
TEST_F(FileShareClientTest, SnapshotNotFoundErrorCode)
{
auto shareSnapshot = m_shareClient->WithSnapshot("2025-02-04T10:17:47.0000000Z");
Files::Shares::Models::DeleteShareResult deleteResult;
EXPECT_NO_THROW(deleteResult = shareSnapshot.DeleteIfExists().Value);
EXPECT_FALSE(deleteResult.Deleted);
}
TEST_F(FileShareClientTest, ShareMetadata) TEST_F(FileShareClientTest, ShareMetadata)
{ {
auto metadata1 = RandomMetadata(); auto metadata1 = RandomMetadata();

View File

@ -1,14 +1,14 @@
# Release History # Release History
## 12.5.0-beta.1 (Unreleased) ## 12.5.0-beta.1 (2025-06-11)
### Features Added ### Features Added
- Added more useful error message when the SDK encounters an x-ms-version mis-match issue.
### Breaking Changes ### Breaking Changes
### Bugs Fixed - `QueueProperties.ApproximateMessageCount` is deprecated. The value is `-1` if the value exceeds `INT32_MAX`. Use `QueueProperties.ApproximateMessageCountLong` instead.
### Other Changes
## 12.4.0 (2024-09-17) ## 12.4.0 (2024-09-17)

View File

@ -9,6 +9,7 @@
#pragma once #pragma once
#include "azure/storage/queues/queue_options.hpp" #include "azure/storage/queues/queue_options.hpp"
#include "azure/storage/queues/queue_responses.hpp"
#include <azure/core/credentials/credentials.hpp> #include <azure/core/credentials/credentials.hpp>
#include <azure/storage/common/storage_credential.hpp> #include <azure/storage/common/storage_credential.hpp>

View File

@ -20,6 +20,32 @@ namespace Azure { namespace Storage { namespace Queues {
class QueueServiceClient; class QueueServiceClient;
namespace Models {
/**
* @brief Response type for #Azure::Storage::Queues::QueueClient::GetProperties.
*/
struct QueueProperties final
{
/**
* A set of name-value pairs associated with this queue.
*/
Core::CaseInsensitiveMap Metadata;
/**
* The approximate number of messages in the queue. This number is not lower than the actual
* number of messages in the queue, but could be higher.
*
* This field is deprecated. The value is -1 if the value exceeds
* INT32_MAX.Use ApproximateMessageCountLong instead.
*/
std::int32_t ApproximateMessageCount = std::int32_t();
/**
* The approximate number of messages in the queue. This number is not lower than the actual
* number of messages in the queue, but could be higher.
*/
std::int64_t ApproximateMessageCountLong = std::int64_t();
};
} // namespace Models
/** /**
* @brief Response type for #Azure::Storage::Queues::QueueServiceClient::ListQueues. * @brief Response type for #Azure::Storage::Queues::QueueServiceClient::ListQueues.
*/ */

View File

@ -283,21 +283,23 @@ namespace Azure { namespace Storage { namespace Queues {
*/ */
bool Deleted = true; bool Deleted = true;
}; };
/** namespace _detail {
* @brief Response type for #Azure::Storage::Queues::QueueClient::GetProperties.
*/
struct QueueProperties final
{
/** /**
* A set of name-value pairs associated with this queue. * @brief Response type for #Azure::Storage::Queues::QueueClient::GetProperties.
*/ */
Core::CaseInsensitiveMap Metadata; struct QueueProperties final
/** {
* The approximate number of messages in the queue. This number is not lower than the actual /**
* number of messages in the queue, but could be higher. * A set of name-value pairs associated with this queue.
*/ */
std::int32_t ApproximateMessageCount = std::int32_t(); Core::CaseInsensitiveMap Metadata;
}; /**
* The approximate number of messages in the queue. This number is not lower than the actual
* number of messages in the queue, but could be higher.
*/
std::int64_t ApproximateMessageCount = std::int64_t();
};
} // namespace _detail
/** /**
* @brief Response type for #Azure::Storage::Queues::QueueClient::SetMetadata. * @brief Response type for #Azure::Storage::Queues::QueueClient::SetMetadata.
*/ */
@ -554,7 +556,7 @@ namespace Azure { namespace Storage { namespace Queues {
struct GetQueuePropertiesOptions final struct GetQueuePropertiesOptions final
{ {
}; };
static Response<Models::QueueProperties> GetProperties( static Response<Models::_detail::QueueProperties> GetProperties(
Core::Http::_internal::HttpPipeline& pipeline, Core::Http::_internal::HttpPipeline& pipeline,
const Core::Url& url, const Core::Url& url,
const GetQueuePropertiesOptions& options, const GetQueuePropertiesOptions& options,

View File

@ -165,8 +165,22 @@ namespace Azure { namespace Storage { namespace Queues {
{ {
(void)options; (void)options;
_detail::QueueClient::GetQueuePropertiesOptions protocolLayerOptions; _detail::QueueClient::GetQueuePropertiesOptions protocolLayerOptions;
return _detail::QueueClient::GetProperties( auto ret = _detail::QueueClient::GetProperties(
*m_pipeline, m_queueUrl, protocolLayerOptions, context); *m_pipeline, m_queueUrl, protocolLayerOptions, context);
Models::QueueProperties queueProperties;
queueProperties.Metadata = std::move(ret.Value.Metadata);
if (ret.Value.ApproximateMessageCount > static_cast<int64_t>(INT32_MAX))
{
queueProperties.ApproximateMessageCount = -1;
}
else
{
queueProperties.ApproximateMessageCount
= static_cast<std::int32_t>(ret.Value.ApproximateMessageCount);
}
queueProperties.ApproximateMessageCountLong = ret.Value.ApproximateMessageCount;
return Azure::Response<Models::QueueProperties>(
std::move(queueProperties), std::move(ret.RawResponse));
} }
Azure::Response<Models::SetQueueMetadataResult> QueueClient::SetMetadata( Azure::Response<Models::SetQueueMetadataResult> QueueClient::SetMetadata(

View File

@ -698,7 +698,7 @@ namespace Azure { namespace Storage { namespace Queues {
Models::DeleteQueueResult response; Models::DeleteQueueResult response;
return Response<Models::DeleteQueueResult>(std::move(response), std::move(pRawResponse)); return Response<Models::DeleteQueueResult>(std::move(response), std::move(pRawResponse));
} }
Response<Models::QueueProperties> QueueClient::GetProperties( Response<Models::_detail::QueueProperties> QueueClient::GetProperties(
Core::Http::_internal::HttpPipeline& pipeline, Core::Http::_internal::HttpPipeline& pipeline,
const Core::Url& url, const Core::Url& url,
const GetQueuePropertiesOptions& options, const GetQueuePropertiesOptions& options,
@ -714,7 +714,7 @@ namespace Azure { namespace Storage { namespace Queues {
{ {
throw StorageException::CreateFromResponse(std::move(pRawResponse)); throw StorageException::CreateFromResponse(std::move(pRawResponse));
} }
Models::QueueProperties response; Models::_detail::QueueProperties response;
for (auto i = pRawResponse->GetHeaders().lower_bound("x-ms-meta-"); for (auto i = pRawResponse->GetHeaders().lower_bound("x-ms-meta-");
i != pRawResponse->GetHeaders().end() && i->first.substr(0, 10) == "x-ms-meta-"; i != pRawResponse->GetHeaders().end() && i->first.substr(0, 10) == "x-ms-meta-";
++i) ++i)
@ -722,8 +722,9 @@ namespace Azure { namespace Storage { namespace Queues {
response.Metadata.emplace(i->first.substr(10), i->second); response.Metadata.emplace(i->first.substr(10), i->second);
} }
response.ApproximateMessageCount response.ApproximateMessageCount
= std::stoi(pRawResponse->GetHeaders().at("x-ms-approximate-messages-count")); = std::stoll(pRawResponse->GetHeaders().at("x-ms-approximate-messages-count"));
return Response<Models::QueueProperties>(std::move(response), std::move(pRawResponse)); return Response<Models::_detail::QueueProperties>(
std::move(response), std::move(pRawResponse));
} }
Response<Models::SetQueueMetadataResult> QueueClient::SetMetadata( Response<Models::SetQueueMetadataResult> QueueClient::SetMetadata(
Core::Http::_internal::HttpPipeline& pipeline, Core::Http::_internal::HttpPipeline& pipeline,

View File

@ -278,6 +278,7 @@ directive:
"type": "object", "type": "object",
"x-ms-client-name": "QueueProperties", "x-ms-client-name": "QueueProperties",
"x-ms-sealed": false, "x-ms-sealed": false,
"x-namespace": "_detail",
"properties": {"__placeHolder": {"type": "integer"}} "properties": {"__placeHolder": {"type": "integer"}}
}; };
``` ```

View File

@ -185,6 +185,39 @@ namespace Azure { namespace Storage { namespace Test {
EXPECT_TRUE(properties.Metadata.empty()); EXPECT_TRUE(properties.Metadata.empty());
} }
TEST_F(QueueClientTest, ApproximateMessageCount)
{
int messageCount = 0;
m_queueClient->EnqueueMessage("test");
++messageCount;
auto res = m_queueClient->GetProperties();
EXPECT_EQ(res.Value.ApproximateMessageCount, messageCount);
EXPECT_EQ(res.Value.ApproximateMessageCountLong, messageCount);
for (; messageCount < 10; ++messageCount)
{
m_queueClient->EnqueueMessage("test");
}
res = m_queueClient->GetProperties();
EXPECT_EQ(res.Value.ApproximateMessageCount, messageCount);
EXPECT_EQ(res.Value.ApproximateMessageCountLong, messageCount);
}
TEST_F(QueueClientTest, ApproximateMessageCountLong_PLAYBACKONLY_)
{
// Hardcode the x-ms-approximate-messages-count header to INT32_MAX in recording file.
m_queueClient->EnqueueMessage("test");
// Mock the recording file to INT32_MAX
auto res = m_queueClient->GetProperties();
EXPECT_EQ(res.Value.ApproximateMessageCount, INT32_MAX);
EXPECT_EQ(res.Value.ApproximateMessageCountLong, INT32_MAX);
// Hardcode the x-ms-approximate-messages-count header to INT64_MAX in recording file.
res = m_queueClient->GetProperties();
EXPECT_EQ(res.Value.ApproximateMessageCount, -1);
EXPECT_EQ(res.Value.ApproximateMessageCountLong, INT64_MAX);
}
TEST_F(QueueClientTest, AccessControlList_LIVEONLY_) TEST_F(QueueClientTest, AccessControlList_LIVEONLY_)
{ {
auto clientOptions = InitStorageClientOptions<Queues::QueueClientOptions>(); auto clientOptions = InitStorageClientOptions<Queues::QueueClientOptions>();