From 74ce7ec0d1c9669390fbc4b83d072f879fcfb901 Mon Sep 17 00:00:00 2001 From: Jinming Hu Date: Sat, 24 Oct 2026 10:23:53 +1100 Subject: [PATCH 01/13] Migrate Queue private protocol layer to TypeSpec Extract only azure-storage-queues from the combined Storage migration. Preserve public headers and reuse the existing Storage pipeline and models. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- sdk/storage/azure-storage-queues/CHANGELOG.md | 2 + .../azure-storage-queues/CMakeLists.txt | 8 +- .../src/generated/queues.cpp | 14 + .../src/generated/queues_client.cpp | 3423 +++++++++++++++++ .../src/generated/queues_client.hpp | 276 ++ .../src/generated/queues_client_options.hpp | 344 ++ .../src/generated/queues_models.hpp | 765 ++++ .../src/private/queue_model_conversions.hpp | 189 + .../src/private/queue_protocol.hpp | 76 + .../azure-storage-queues/src/queue_client.cpp | 211 +- .../src/queue_responses.cpp | 6 + .../src/queue_service_client.cpp | 110 +- .../azure-storage-queues/src/rest_client.cpp | 1550 -------- .../azure-storage-queues/swagger/README.md | 5 +- .../test/ut/CMakeLists.txt | 1 + .../test/ut/queue_protocol_test.cpp | 652 ++++ .../azure-storage-queues/tsp-location.yaml | 3 + .../azure-storage-queues/tspconfig.yaml | 11 + .../azure-storage-queues/typespec/README.md | 75 + .../typespec/generate.ps1 | 69 + 20 files changed, 6127 insertions(+), 1663 deletions(-) create mode 100644 sdk/storage/azure-storage-queues/src/generated/queues.cpp create mode 100644 sdk/storage/azure-storage-queues/src/generated/queues_client.cpp create mode 100644 sdk/storage/azure-storage-queues/src/generated/queues_client.hpp create mode 100644 sdk/storage/azure-storage-queues/src/generated/queues_client_options.hpp create mode 100644 sdk/storage/azure-storage-queues/src/generated/queues_models.hpp create mode 100644 sdk/storage/azure-storage-queues/src/private/queue_model_conversions.hpp create mode 100644 sdk/storage/azure-storage-queues/src/private/queue_protocol.hpp delete mode 100644 sdk/storage/azure-storage-queues/src/rest_client.cpp create mode 100644 sdk/storage/azure-storage-queues/test/ut/queue_protocol_test.cpp create mode 100644 sdk/storage/azure-storage-queues/tsp-location.yaml create mode 100644 sdk/storage/azure-storage-queues/tspconfig.yaml create mode 100644 sdk/storage/azure-storage-queues/typespec/README.md create mode 100644 sdk/storage/azure-storage-queues/typespec/generate.ps1 diff --git a/sdk/storage/azure-storage-queues/CHANGELOG.md b/sdk/storage/azure-storage-queues/CHANGELOG.md index cc8128ebc1..2fb162dd7a 100644 --- a/sdk/storage/azure-storage-queues/CHANGELOG.md +++ b/sdk/storage/azure-storage-queues/CHANGELOG.md @@ -10,6 +10,8 @@ ### Other Changes +- Migrated the internal REST protocol layer to TypeSpec generation without changing public APIs. + ## 12.8.0 (2026-09-29) ### Other Changes diff --git a/sdk/storage/azure-storage-queues/CMakeLists.txt b/sdk/storage/azure-storage-queues/CMakeLists.txt index 7f018f19dc..c17519a88b 100644 --- a/sdk/storage/azure-storage-queues/CMakeLists.txt +++ b/sdk/storage/azure-storage-queues/CMakeLists.txt @@ -55,12 +55,18 @@ set( set( AZURE_STORAGE_QUEUES_SOURCE src/private/package_version.hpp + src/private/queue_model_conversions.hpp + src/private/queue_protocol.hpp + src/generated/queues_client.hpp + src/generated/queues_client_options.hpp + src/generated/queues_models.hpp + src/generated/queues_client.cpp + src/generated/queues.cpp src/queue_client.cpp src/queue_options.cpp src/queue_responses.cpp src/queue_sas_builder.cpp src/queue_service_client.cpp - src/rest_client.cpp ) add_library(azure-storage-queues ${AZURE_STORAGE_QUEUES_HEADER} ${AZURE_STORAGE_QUEUES_SOURCE}) diff --git a/sdk/storage/azure-storage-queues/src/generated/queues.cpp b/sdk/storage/azure-storage-queues/src/generated/queues.cpp new file mode 100644 index 0000000000..feb5a1ecd2 --- /dev/null +++ b/sdk/storage/azure-storage-queues/src/generated/queues.cpp @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// Code generated by Microsoft (R) TypeSpec Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +#include "queues_models.hpp" + +using namespace Azure::Storage::Queues::_detail::Models; + +const GeoReplicationStatus GeoReplicationStatus::Live{"live"}; +const GeoReplicationStatus GeoReplicationStatus::Bootstrap{"bootstrap"}; +const GeoReplicationStatus GeoReplicationStatus::Unavailable{"unavailable"}; + +const ListQueuesIncludeType ListQueuesIncludeType::Metadata{"metadata"}; diff --git a/sdk/storage/azure-storage-queues/src/generated/queues_client.cpp b/sdk/storage/azure-storage-queues/src/generated/queues_client.cpp new file mode 100644 index 0000000000..95232707f2 --- /dev/null +++ b/sdk/storage/azure-storage-queues/src/generated/queues_client.cpp @@ -0,0 +1,3423 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// Code generated by Microsoft (R) TypeSpec Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +#include "queues_client.hpp" + +#include "../private/package_version.hpp" + +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace Azure::Storage::Queues::_detail; + +QueuesClient::QueuesClient( + Core::Url endpoint, + std::shared_ptr pipeline, + const QueuesClientOptions& options) + : m_pipeline(std::move(pipeline)), m_url(std::move(endpoint)), m_xMsVersion(options.XMsVersion) +{ + if (!m_pipeline) + { + throw std::invalid_argument("pipeline must not be null"); + } +} + +std::string QueuesClient::GetUrl() const { return m_url.GetAbsoluteUrl(); } + +Azure::Response +QueuesClient::SetPropertiesService( + const Models::QueueServiceProperties& queueServiceProperties, + const QueuesClientSetPropertiesServiceOptions& options, + const Core::Context& context) const +{ + auto url = m_url; + + url.AppendQueryParameter("comp", "properties"); + url.AppendQueryParameter("restype", "service"); + + if (options.Timeout.HasValue()) + { + url.AppendQueryParameter("timeout", std::to_string(options.Timeout.Value())); + } + + std::string xmlBody; + { + using namespace _internal; + XmlWriter writer; + + writer.Write(XmlNode{XmlNodeType::StartTag, "StorageServiceProperties"}); + if (queueServiceProperties.Logging.HasValue()) + { + writer.Write(XmlNode{XmlNodeType::StartTag, "Logging"}); + writer.Write(XmlNode{ + XmlNodeType::StartTag, "Version", queueServiceProperties.Logging.Value().Version}); + writer.Write(XmlNode{ + XmlNodeType::StartTag, + "Delete", + queueServiceProperties.Logging.Value().Delete ? "true" : "false"}); + writer.Write(XmlNode{ + XmlNodeType::StartTag, + "Read", + queueServiceProperties.Logging.Value().Read ? "true" : "false"}); + writer.Write(XmlNode{ + XmlNodeType::StartTag, + "Write", + queueServiceProperties.Logging.Value().Write ? "true" : "false"}); + writer.Write(XmlNode{XmlNodeType::StartTag, "RetentionPolicy"}); + writer.Write(XmlNode{ + XmlNodeType::StartTag, + "Enabled", + queueServiceProperties.Logging.Value().RetentionPolicy.Enabled ? "true" : "false"}); + if (queueServiceProperties.Logging.Value().RetentionPolicy.Days.HasValue()) + { + writer.Write(XmlNode{ + XmlNodeType::StartTag, + "Days", + std::to_string(queueServiceProperties.Logging.Value().RetentionPolicy.Days.Value())}); + } + writer.Write(XmlNode{XmlNodeType::EndTag}); + writer.Write(XmlNode{XmlNodeType::EndTag}); + } + if (queueServiceProperties.HourMetrics.HasValue()) + { + writer.Write(XmlNode{XmlNodeType::StartTag, "HourMetrics"}); + if (queueServiceProperties.HourMetrics.Value().Version.HasValue()) + { + writer.Write(XmlNode{ + XmlNodeType::StartTag, + "Version", + queueServiceProperties.HourMetrics.Value().Version.Value()}); + } + writer.Write(XmlNode{ + XmlNodeType::StartTag, + "Enabled", + queueServiceProperties.HourMetrics.Value().Enabled ? "true" : "false"}); + if (queueServiceProperties.HourMetrics.Value().IncludeApis.HasValue()) + { + writer.Write(XmlNode{ + XmlNodeType::StartTag, + "IncludeAPIs", + queueServiceProperties.HourMetrics.Value().IncludeApis.Value() ? "true" : "false"}); + } + if (queueServiceProperties.HourMetrics.Value().RetentionPolicy.HasValue()) + { + writer.Write(XmlNode{XmlNodeType::StartTag, "RetentionPolicy"}); + writer.Write(XmlNode{ + XmlNodeType::StartTag, + "Enabled", + queueServiceProperties.HourMetrics.Value().RetentionPolicy.Value().Enabled ? "true" + : "false"}); + if (queueServiceProperties.HourMetrics.Value().RetentionPolicy.Value().Days.HasValue()) + { + writer.Write(XmlNode{ + XmlNodeType::StartTag, + "Days", + std::to_string(queueServiceProperties.HourMetrics.Value() + .RetentionPolicy.Value() + .Days.Value())}); + } + writer.Write(XmlNode{XmlNodeType::EndTag}); + } + writer.Write(XmlNode{XmlNodeType::EndTag}); + } + if (queueServiceProperties.MinuteMetrics.HasValue()) + { + writer.Write(XmlNode{XmlNodeType::StartTag, "MinuteMetrics"}); + if (queueServiceProperties.MinuteMetrics.Value().Version.HasValue()) + { + writer.Write(XmlNode{ + XmlNodeType::StartTag, + "Version", + queueServiceProperties.MinuteMetrics.Value().Version.Value()}); + } + writer.Write(XmlNode{ + XmlNodeType::StartTag, + "Enabled", + queueServiceProperties.MinuteMetrics.Value().Enabled ? "true" : "false"}); + if (queueServiceProperties.MinuteMetrics.Value().IncludeApis.HasValue()) + { + writer.Write(XmlNode{ + XmlNodeType::StartTag, + "IncludeAPIs", + queueServiceProperties.MinuteMetrics.Value().IncludeApis.Value() ? "true" : "false"}); + } + if (queueServiceProperties.MinuteMetrics.Value().RetentionPolicy.HasValue()) + { + writer.Write(XmlNode{XmlNodeType::StartTag, "RetentionPolicy"}); + writer.Write(XmlNode{ + XmlNodeType::StartTag, + "Enabled", + queueServiceProperties.MinuteMetrics.Value().RetentionPolicy.Value().Enabled + ? "true" + : "false"}); + if (queueServiceProperties.MinuteMetrics.Value().RetentionPolicy.Value().Days.HasValue()) + { + writer.Write(XmlNode{ + XmlNodeType::StartTag, + "Days", + std::to_string(queueServiceProperties.MinuteMetrics.Value() + .RetentionPolicy.Value() + .Days.Value())}); + } + writer.Write(XmlNode{XmlNodeType::EndTag}); + } + writer.Write(XmlNode{XmlNodeType::EndTag}); + } + if (queueServiceProperties.Cors.HasValue()) + { + writer.Write(XmlNode{XmlNodeType::StartTag, "Cors"}); + for (auto const& item : queueServiceProperties.Cors.Value()) + { + writer.Write(XmlNode{XmlNodeType::StartTag, "CorsRule"}); + writer.Write(XmlNode{XmlNodeType::StartTag, "AllowedOrigins", item.AllowedOrigins}); + writer.Write(XmlNode{XmlNodeType::StartTag, "AllowedMethods", item.AllowedMethods}); + writer.Write(XmlNode{XmlNodeType::StartTag, "AllowedHeaders", item.AllowedHeaders}); + writer.Write(XmlNode{XmlNodeType::StartTag, "ExposedHeaders", item.ExposedHeaders}); + writer.Write(XmlNode{ + XmlNodeType::StartTag, "MaxAgeInSeconds", std::to_string(item.MaxAgeInSeconds)}); + writer.Write(XmlNode{XmlNodeType::EndTag}); + } + writer.Write(XmlNode{XmlNodeType::EndTag}); + } + writer.Write(XmlNode{XmlNodeType::EndTag}); + + writer.Write(XmlNode{XmlNodeType::End}); + xmlBody = writer.GetDocument(); + } + + Core::IO::MemoryBodyStream requestBody( + reinterpret_cast(xmlBody.data()), xmlBody.length()); + + Core::Http::Request request(Core::Http::HttpMethod::Put, url, &requestBody); + + request.SetHeader("Content-Type", "application/xml"); + + request.SetHeader("x-ms-version", m_xMsVersion); + if (options.XMsClientRequestId.HasValue()) + { + request.SetHeader("x-ms-client-request-id", options.XMsClientRequestId.Value()); + } + + request.SetHeader("Content-Length", std::to_string(requestBody.Length())); + + auto rawResponse = m_pipeline->Send(request, context); + const auto httpStatusCode = rawResponse->GetStatusCode(); + + if (httpStatusCode != Core::Http::HttpStatusCode::Accepted) + { + throw Core::RequestFailedException(rawResponse); + } + + Models::SetPropertiesServiceResult response{}; + { + auto header = rawResponse->GetHeaders().find("x-ms-version"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.Version = headerValue; + } + else + { + throw std::out_of_range("Missing required response header: x-ms-version"); + } + } + { + auto header = rawResponse->GetHeaders().find("x-ms-request-id"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.RequestId = headerValue; + } + } + { + auto header = rawResponse->GetHeaders().find("x-ms-client-request-id"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.ClientRequestId = headerValue; + } + } + { + auto header = rawResponse->GetHeaders().find("Date"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.Date = DateTime::Parse(headerValue, DateTime::DateFormat::Rfc1123); + } + else + { + throw std::out_of_range("Missing required response header: Date"); + } + } + + return Response(std::move(response), std::move(rawResponse)); +} + +Azure::Response +QueuesClient::GetPropertiesService( + const QueuesClientGetPropertiesServiceOptions& options, + const Core::Context& context) const +{ + auto url = m_url; + + url.AppendQueryParameter("comp", "properties"); + url.AppendQueryParameter("restype", "service"); + + if (options.Timeout.HasValue()) + { + url.AppendQueryParameter("timeout", std::to_string(options.Timeout.Value())); + } + + Core::Http::Request request(Core::Http::HttpMethod::Get, url); + + request.SetHeader("Accept", "application/xml"); + + request.SetHeader("x-ms-version", m_xMsVersion); + if (options.XMsClientRequestId.HasValue()) + { + request.SetHeader("x-ms-client-request-id", options.XMsClientRequestId.Value()); + } + + auto rawResponse = m_pipeline->Send(request, context); + const auto httpStatusCode = rawResponse->GetStatusCode(); + + if (httpStatusCode != Core::Http::HttpStatusCode::Ok) + { + throw Core::RequestFailedException(rawResponse); + } + + Models::GetPropertiesServiceResult response{}; + { + using namespace _internal; + const auto& responseBody = rawResponse->GetBody(); + XmlReader reader(reinterpret_cast(responseBody.data()), responseBody.size()); + try + { + for (auto node = reader.Read(); node.Type != XmlNodeType::End; node = reader.Read()) + { + if (node.Type == XmlNodeType::StartTag && node.Name == "StorageServiceProperties") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::StartTag || node.Type == XmlNodeType::Attribute) + { + if (node.Type == XmlNodeType::StartTag && node.Name == "Logging") + { + if (!response.Logging.HasValue()) + { + response.Logging = Models::Logging{}; + } + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::StartTag || node.Type == XmlNodeType::Attribute) + { + if (node.Type == XmlNodeType::StartTag && node.Name == "Version") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + response.Logging.Value().Version = node.Value; + } + } + } + else if (node.Type == XmlNodeType::StartTag && node.Name == "Delete") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + response.Logging.Value().Delete = (node.Value == "true"); + } + } + } + else if (node.Type == XmlNodeType::StartTag && node.Name == "Read") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + response.Logging.Value().Read = (node.Value == "true"); + } + } + } + else if (node.Type == XmlNodeType::StartTag && node.Name == "Write") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + response.Logging.Value().Write = (node.Value == "true"); + } + } + } + else if (node.Type == XmlNodeType::StartTag && node.Name == "RetentionPolicy") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::StartTag + || node.Type == XmlNodeType::Attribute) + { + if (node.Type == XmlNodeType::StartTag && node.Name == "Enabled") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + response.Logging.Value().RetentionPolicy.Enabled + = (node.Value == "true"); + } + } + } + else if (node.Type == XmlNodeType::StartTag && node.Name == "Days") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + response.Logging.Value().RetentionPolicy.Days + = std::stoi(node.Value); + } + } + } + else if (node.Type == XmlNodeType::StartTag) + { + int depth = 1; + while (depth > 0) + { + node = reader.Read(); + if (node.Type == XmlNodeType::End) + { + throw std::runtime_error("Unexpected end of XML."); + } + if (node.Type == XmlNodeType::StartTag) + { + ++depth; + } + else if (node.Type == XmlNodeType::EndTag) + { + --depth; + } + } + } + } + } + } + else if (node.Type == XmlNodeType::StartTag) + { + int depth = 1; + while (depth > 0) + { + node = reader.Read(); + if (node.Type == XmlNodeType::End) + { + throw std::runtime_error("Unexpected end of XML."); + } + if (node.Type == XmlNodeType::StartTag) + { + ++depth; + } + else if (node.Type == XmlNodeType::EndTag) + { + --depth; + } + } + } + } + } + } + else if (node.Type == XmlNodeType::StartTag && node.Name == "HourMetrics") + { + if (!response.HourMetrics.HasValue()) + { + response.HourMetrics = Models::Metrics{}; + } + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::StartTag || node.Type == XmlNodeType::Attribute) + { + if (node.Type == XmlNodeType::StartTag && node.Name == "Version") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + response.HourMetrics.Value().Version = node.Value; + } + } + } + else if (node.Type == XmlNodeType::StartTag && node.Name == "Enabled") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + response.HourMetrics.Value().Enabled = (node.Value == "true"); + } + } + } + else if (node.Type == XmlNodeType::StartTag && node.Name == "IncludeAPIs") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + response.HourMetrics.Value().IncludeApis = (node.Value == "true"); + } + } + } + else if (node.Type == XmlNodeType::StartTag && node.Name == "RetentionPolicy") + { + if (!response.HourMetrics.Value().RetentionPolicy.HasValue()) + { + response.HourMetrics.Value().RetentionPolicy = Models::RetentionPolicy{}; + } + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::StartTag + || node.Type == XmlNodeType::Attribute) + { + if (node.Type == XmlNodeType::StartTag && node.Name == "Enabled") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + response.HourMetrics.Value().RetentionPolicy.Value().Enabled + = (node.Value == "true"); + } + } + } + else if (node.Type == XmlNodeType::StartTag && node.Name == "Days") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + response.HourMetrics.Value().RetentionPolicy.Value().Days + = std::stoi(node.Value); + } + } + } + else if (node.Type == XmlNodeType::StartTag) + { + int depth = 1; + while (depth > 0) + { + node = reader.Read(); + if (node.Type == XmlNodeType::End) + { + throw std::runtime_error("Unexpected end of XML."); + } + if (node.Type == XmlNodeType::StartTag) + { + ++depth; + } + else if (node.Type == XmlNodeType::EndTag) + { + --depth; + } + } + } + } + } + } + else if (node.Type == XmlNodeType::StartTag) + { + int depth = 1; + while (depth > 0) + { + node = reader.Read(); + if (node.Type == XmlNodeType::End) + { + throw std::runtime_error("Unexpected end of XML."); + } + if (node.Type == XmlNodeType::StartTag) + { + ++depth; + } + else if (node.Type == XmlNodeType::EndTag) + { + --depth; + } + } + } + } + } + } + else if (node.Type == XmlNodeType::StartTag && node.Name == "MinuteMetrics") + { + if (!response.MinuteMetrics.HasValue()) + { + response.MinuteMetrics = Models::Metrics{}; + } + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::StartTag || node.Type == XmlNodeType::Attribute) + { + if (node.Type == XmlNodeType::StartTag && node.Name == "Version") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + response.MinuteMetrics.Value().Version = node.Value; + } + } + } + else if (node.Type == XmlNodeType::StartTag && node.Name == "Enabled") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + response.MinuteMetrics.Value().Enabled = (node.Value == "true"); + } + } + } + else if (node.Type == XmlNodeType::StartTag && node.Name == "IncludeAPIs") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + response.MinuteMetrics.Value().IncludeApis = (node.Value == "true"); + } + } + } + else if (node.Type == XmlNodeType::StartTag && node.Name == "RetentionPolicy") + { + if (!response.MinuteMetrics.Value().RetentionPolicy.HasValue()) + { + response.MinuteMetrics.Value().RetentionPolicy = Models::RetentionPolicy{}; + } + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::StartTag + || node.Type == XmlNodeType::Attribute) + { + if (node.Type == XmlNodeType::StartTag && node.Name == "Enabled") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + response.MinuteMetrics.Value().RetentionPolicy.Value().Enabled + = (node.Value == "true"); + } + } + } + else if (node.Type == XmlNodeType::StartTag && node.Name == "Days") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + response.MinuteMetrics.Value().RetentionPolicy.Value().Days + = std::stoi(node.Value); + } + } + } + else if (node.Type == XmlNodeType::StartTag) + { + int depth = 1; + while (depth > 0) + { + node = reader.Read(); + if (node.Type == XmlNodeType::End) + { + throw std::runtime_error("Unexpected end of XML."); + } + if (node.Type == XmlNodeType::StartTag) + { + ++depth; + } + else if (node.Type == XmlNodeType::EndTag) + { + --depth; + } + } + } + } + } + } + else if (node.Type == XmlNodeType::StartTag) + { + int depth = 1; + while (depth > 0) + { + node = reader.Read(); + if (node.Type == XmlNodeType::End) + { + throw std::runtime_error("Unexpected end of XML."); + } + if (node.Type == XmlNodeType::StartTag) + { + ++depth; + } + else if (node.Type == XmlNodeType::EndTag) + { + --depth; + } + } + } + } + } + } + else if (node.Type == XmlNodeType::StartTag && node.Name == "Cors") + { + if (!response.Cors.HasValue()) + { + response.Cors = std::vector{}; + } + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::StartTag && node.Name == "CorsRule") + { + Models::CorsRule responseCorsValueItem{}; + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::StartTag || node.Type == XmlNodeType::Attribute) + { + if (node.Type == XmlNodeType::StartTag && node.Name == "AllowedOrigins") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + responseCorsValueItem.AllowedOrigins = node.Value; + } + } + } + else if ( + node.Type == XmlNodeType::StartTag && node.Name == "AllowedMethods") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + responseCorsValueItem.AllowedMethods = node.Value; + } + } + } + else if ( + node.Type == XmlNodeType::StartTag && node.Name == "AllowedHeaders") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + responseCorsValueItem.AllowedHeaders = node.Value; + } + } + } + else if ( + node.Type == XmlNodeType::StartTag && node.Name == "ExposedHeaders") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + responseCorsValueItem.ExposedHeaders = node.Value; + } + } + } + else if ( + node.Type == XmlNodeType::StartTag && node.Name == "MaxAgeInSeconds") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + responseCorsValueItem.MaxAgeInSeconds = std::stoi(node.Value); + } + } + } + else if (node.Type == XmlNodeType::StartTag) + { + int depth = 1; + while (depth > 0) + { + node = reader.Read(); + if (node.Type == XmlNodeType::End) + { + throw std::runtime_error("Unexpected end of XML."); + } + if (node.Type == XmlNodeType::StartTag) + { + ++depth; + } + else if (node.Type == XmlNodeType::EndTag) + { + --depth; + } + } + } + } + } + response.Cors.Value().emplace_back(std::move(responseCorsValueItem)); + } + else if (node.Type == XmlNodeType::StartTag) + { + int depth = 1; + while (depth > 0) + { + node = reader.Read(); + if (node.Type == XmlNodeType::End) + { + throw std::runtime_error("Unexpected end of XML."); + } + if (node.Type == XmlNodeType::StartTag) + { + ++depth; + } + else if (node.Type == XmlNodeType::EndTag) + { + --depth; + } + } + } + } + } + else if (node.Type == XmlNodeType::StartTag) + { + int depth = 1; + while (depth > 0) + { + node = reader.Read(); + if (node.Type == XmlNodeType::End) + { + throw std::runtime_error("Unexpected end of XML."); + } + if (node.Type == XmlNodeType::StartTag) + { + ++depth; + } + else if (node.Type == XmlNodeType::EndTag) + { + --depth; + } + } + } + } + } + } + } + } + catch (Core::RequestFailedException const&) + { + throw; + } + catch (std::exception const& ex) + { + throw Core::RequestFailedException(ex.what()); + } + } + { + auto header = rawResponse->GetHeaders().find("x-ms-version"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.Version = headerValue; + } + else + { + throw std::out_of_range("Missing required response header: x-ms-version"); + } + } + { + auto header = rawResponse->GetHeaders().find("x-ms-request-id"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.RequestId = headerValue; + } + } + { + auto header = rawResponse->GetHeaders().find("x-ms-client-request-id"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.ClientRequestId = headerValue; + } + } + { + auto header = rawResponse->GetHeaders().find("Date"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.Date = DateTime::Parse(headerValue, DateTime::DateFormat::Rfc1123); + } + else + { + throw std::out_of_range("Missing required response header: Date"); + } + } + + return Response(std::move(response), std::move(rawResponse)); +} + +Azure::Response +QueuesClient::GetStatisticsService( + const QueuesClientGetStatisticsServiceOptions& options, + const Core::Context& context) const +{ + auto url = m_url; + + url.AppendQueryParameter("comp", "stats"); + url.AppendQueryParameter("restype", "service"); + + if (options.Timeout.HasValue()) + { + url.AppendQueryParameter("timeout", std::to_string(options.Timeout.Value())); + } + + Core::Http::Request request(Core::Http::HttpMethod::Get, url); + + request.SetHeader("Accept", "application/xml"); + + request.SetHeader("x-ms-version", m_xMsVersion); + if (options.XMsClientRequestId.HasValue()) + { + request.SetHeader("x-ms-client-request-id", options.XMsClientRequestId.Value()); + } + + auto rawResponse = m_pipeline->Send(request, context); + const auto httpStatusCode = rawResponse->GetStatusCode(); + + if (httpStatusCode != Core::Http::HttpStatusCode::Ok) + { + throw Core::RequestFailedException(rawResponse); + } + + Models::GetStatisticsServiceResult response{}; + { + using namespace _internal; + const auto& responseBody = rawResponse->GetBody(); + XmlReader reader(reinterpret_cast(responseBody.data()), responseBody.size()); + try + { + for (auto node = reader.Read(); node.Type != XmlNodeType::End; node = reader.Read()) + { + if (node.Type == XmlNodeType::StartTag && node.Name == "StorageServiceStats") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::StartTag || node.Type == XmlNodeType::Attribute) + { + if (node.Type == XmlNodeType::StartTag && node.Name == "GeoReplication") + { + if (!response.GeoReplication.HasValue()) + { + response.GeoReplication = Models::GeoReplication{}; + } + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::StartTag || node.Type == XmlNodeType::Attribute) + { + if (node.Type == XmlNodeType::StartTag && node.Name == "Status") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + response.GeoReplication.Value().Status + = Models::GeoReplicationStatus(node.Value); + } + } + } + else if (node.Type == XmlNodeType::StartTag && node.Name == "LastSyncTime") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + response.GeoReplication.Value().LastSyncTime + = DateTime::Parse(node.Value, DateTime::DateFormat::Rfc1123); + } + } + } + else if (node.Type == XmlNodeType::StartTag) + { + int depth = 1; + while (depth > 0) + { + node = reader.Read(); + if (node.Type == XmlNodeType::End) + { + throw std::runtime_error("Unexpected end of XML."); + } + if (node.Type == XmlNodeType::StartTag) + { + ++depth; + } + else if (node.Type == XmlNodeType::EndTag) + { + --depth; + } + } + } + } + } + } + else if (node.Type == XmlNodeType::StartTag) + { + int depth = 1; + while (depth > 0) + { + node = reader.Read(); + if (node.Type == XmlNodeType::End) + { + throw std::runtime_error("Unexpected end of XML."); + } + if (node.Type == XmlNodeType::StartTag) + { + ++depth; + } + else if (node.Type == XmlNodeType::EndTag) + { + --depth; + } + } + } + } + } + } + } + } + catch (Core::RequestFailedException const&) + { + throw; + } + catch (std::exception const& ex) + { + throw Core::RequestFailedException(ex.what()); + } + } + { + auto header = rawResponse->GetHeaders().find("x-ms-version"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.Version = headerValue; + } + else + { + throw std::out_of_range("Missing required response header: x-ms-version"); + } + } + { + auto header = rawResponse->GetHeaders().find("x-ms-request-id"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.RequestId = headerValue; + } + } + { + auto header = rawResponse->GetHeaders().find("x-ms-client-request-id"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.ClientRequestId = headerValue; + } + } + { + auto header = rawResponse->GetHeaders().find("Date"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.Date = DateTime::Parse(headerValue, DateTime::DateFormat::Rfc1123); + } + else + { + throw std::out_of_range("Missing required response header: Date"); + } + } + + return Response(std::move(response), std::move(rawResponse)); +} + +Azure::Response +QueuesClient::GetUserDelegationKeyService( + const Models::KeyInfo& keyInfo, + const QueuesClientGetUserDelegationKeyServiceOptions& options, + const Core::Context& context) const +{ + auto url = m_url; + + url.AppendQueryParameter("comp", "userdelegationkey"); + url.AppendQueryParameter("restype", "service"); + + if (options.Timeout.HasValue()) + { + url.AppendQueryParameter("timeout", std::to_string(options.Timeout.Value())); + } + + std::string xmlBody; + { + using namespace _internal; + XmlWriter writer; + + writer.Write(XmlNode{XmlNodeType::StartTag, "KeyInfo"}); + if (keyInfo.Start.HasValue()) + { + writer.Write(XmlNode{XmlNodeType::StartTag, "Start", keyInfo.Start.Value()}); + } + writer.Write(XmlNode{XmlNodeType::StartTag, "Expiry", keyInfo.Expiry}); + if (keyInfo.DelegatedUserTid.HasValue()) + { + writer.Write( + XmlNode{XmlNodeType::StartTag, "DelegatedUserTid", keyInfo.DelegatedUserTid.Value()}); + } + writer.Write(XmlNode{XmlNodeType::EndTag}); + + writer.Write(XmlNode{XmlNodeType::End}); + xmlBody = writer.GetDocument(); + } + + Core::IO::MemoryBodyStream requestBody( + reinterpret_cast(xmlBody.data()), xmlBody.length()); + + Core::Http::Request request(Core::Http::HttpMethod::Post, url, &requestBody); + + request.SetHeader("Content-Type", "application/xml"); + request.SetHeader("Accept", "application/xml"); + + request.SetHeader("x-ms-version", m_xMsVersion); + if (options.XMsClientRequestId.HasValue()) + { + request.SetHeader("x-ms-client-request-id", options.XMsClientRequestId.Value()); + } + + request.SetHeader("Content-Length", std::to_string(requestBody.Length())); + + auto rawResponse = m_pipeline->Send(request, context); + const auto httpStatusCode = rawResponse->GetStatusCode(); + + if (httpStatusCode != Core::Http::HttpStatusCode::Ok) + { + throw Core::RequestFailedException(rawResponse); + } + + Models::GetUserDelegationKeyServiceResult response{}; + { + using namespace _internal; + const auto& responseBody = rawResponse->GetBody(); + XmlReader reader(reinterpret_cast(responseBody.data()), responseBody.size()); + try + { + for (auto node = reader.Read(); node.Type != XmlNodeType::End; node = reader.Read()) + { + if (node.Type == XmlNodeType::StartTag && node.Name == "UserDelegationKey") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::StartTag || node.Type == XmlNodeType::Attribute) + { + if (node.Type == XmlNodeType::StartTag && node.Name == "SignedOid") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + response.SignedOid = node.Value; + } + } + } + else if (node.Type == XmlNodeType::StartTag && node.Name == "SignedTid") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + response.SignedTid = node.Value; + } + } + } + else if (node.Type == XmlNodeType::StartTag && node.Name == "SignedStart") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + response.SignedStart + = DateTime::Parse(node.Value, DateTime::DateFormat::Rfc3339); + } + } + } + else if (node.Type == XmlNodeType::StartTag && node.Name == "SignedExpiry") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + response.SignedExpiry + = DateTime::Parse(node.Value, DateTime::DateFormat::Rfc3339); + } + } + } + else if (node.Type == XmlNodeType::StartTag && node.Name == "SignedService") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + response.SignedService = node.Value; + } + } + } + else if (node.Type == XmlNodeType::StartTag && node.Name == "SignedVersion") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + response.SignedVersion = node.Value; + } + } + } + else if (node.Type == XmlNodeType::StartTag && node.Name == "SignedDelegatedUserTid") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + response.SignedDelegatedUserTid = node.Value; + } + } + } + else if (node.Type == XmlNodeType::StartTag && node.Name == "Value") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + response.Value = node.Value; + } + } + } + else if (node.Type == XmlNodeType::StartTag) + { + int depth = 1; + while (depth > 0) + { + node = reader.Read(); + if (node.Type == XmlNodeType::End) + { + throw std::runtime_error("Unexpected end of XML."); + } + if (node.Type == XmlNodeType::StartTag) + { + ++depth; + } + else if (node.Type == XmlNodeType::EndTag) + { + --depth; + } + } + } + } + } + } + } + } + catch (Core::RequestFailedException const&) + { + throw; + } + catch (std::exception const& ex) + { + throw Core::RequestFailedException(ex.what()); + } + } + { + auto header = rawResponse->GetHeaders().find("x-ms-version"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.Version = headerValue; + } + else + { + throw std::out_of_range("Missing required response header: x-ms-version"); + } + } + { + auto header = rawResponse->GetHeaders().find("x-ms-request-id"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.RequestId = headerValue; + } + } + { + auto header = rawResponse->GetHeaders().find("x-ms-client-request-id"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.ClientRequestId = headerValue; + } + } + { + auto header = rawResponse->GetHeaders().find("Date"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.Date = DateTime::Parse(headerValue, DateTime::DateFormat::Rfc1123); + } + else + { + throw std::out_of_range("Missing required response header: Date"); + } + } + + return Response( + std::move(response), std::move(rawResponse)); +} + +Azure::Response +QueuesClient::GetQueuesService( + const QueuesClientGetQueuesServiceOptions& options, + const Core::Context& context) const +{ + auto url = m_url; + + url.AppendQueryParameter("comp", "list"); + + if (options.Prefix.HasValue()) + { + url.AppendQueryParameter("prefix", Core::Url::Encode(options.Prefix.Value())); + } + + if (options.Marker.HasValue()) + { + url.AppendQueryParameter("marker", Core::Url::Encode(options.Marker.Value())); + } + + if (options.Maxresults.HasValue()) + { + url.AppendQueryParameter("maxresults", std::to_string(options.Maxresults.Value())); + } + + if (options.Timeout.HasValue()) + { + url.AppendQueryParameter("timeout", std::to_string(options.Timeout.Value())); + } + + if (options.Include.HasValue()) + { + std::string valueStr; + const size_t size = options.Include.Value().size(); + for (size_t i = 0; i < size; ++i) + { + if (i != 0) + { + valueStr += ','; + } + + valueStr += Core::Url::Encode(options.Include.Value()[i].ToString()); + } + + url.AppendQueryParameter("include", valueStr); + } + + Core::Http::Request request(Core::Http::HttpMethod::Get, url); + + request.SetHeader("Accept", "application/xml"); + + request.SetHeader("x-ms-version", m_xMsVersion); + if (options.XMsClientRequestId.HasValue()) + { + request.SetHeader("x-ms-client-request-id", options.XMsClientRequestId.Value()); + } + + auto rawResponse = m_pipeline->Send(request, context); + const auto httpStatusCode = rawResponse->GetStatusCode(); + + if (httpStatusCode != Core::Http::HttpStatusCode::Ok) + { + throw Core::RequestFailedException(rawResponse); + } + + Models::GetQueuesServiceResult response{}; + { + using namespace _internal; + const auto& responseBody = rawResponse->GetBody(); + XmlReader reader(reinterpret_cast(responseBody.data()), responseBody.size()); + try + { + for (auto node = reader.Read(); node.Type != XmlNodeType::End; node = reader.Read()) + { + if (node.Type == XmlNodeType::StartTag && node.Name == "EnumerationResults") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::StartTag || node.Type == XmlNodeType::Attribute) + { + if (node.Type == XmlNodeType::Attribute && node.Name == "ServiceEndpoint") + { + response.ServiceEndpoint = node.Value; + } + else if (node.Type == XmlNodeType::StartTag && node.Name == "Prefix") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + response.Prefix = node.Value; + } + } + } + else if (node.Type == XmlNodeType::StartTag && node.Name == "Marker") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + response.Marker = node.Value; + } + } + } + else if (node.Type == XmlNodeType::StartTag && node.Name == "MaxResults") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + response.MaxResults = std::stoi(node.Value); + } + } + } + else if (node.Type == XmlNodeType::StartTag && node.Name == "Queues") + { + if (!response.QueueItems.HasValue()) + { + response.QueueItems = std::vector{}; + } + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::StartTag && node.Name == "Queue") + { + Models::QueueItem responseQueueItemsValueItem{}; + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::StartTag || node.Type == XmlNodeType::Attribute) + { + if (node.Type == XmlNodeType::StartTag && node.Name == "Name") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + responseQueueItemsValueItem.Name = node.Value; + } + } + } + else if (node.Type == XmlNodeType::StartTag && node.Name == "Metadata") + { + if (!responseQueueItemsValueItem.Metadata.HasValue()) + { + responseQueueItemsValueItem.Metadata + = std::map{}; + } + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::StartTag) + { + auto const key = node.Name; + responseQueueItemsValueItem.Metadata.Value()[key] = std::string{}; + for (node = reader.Read(); node.Type != XmlNodeType::EndTag + && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + responseQueueItemsValueItem.Metadata.Value()[key] = node.Value; + } + } + } + } + } + else if (node.Type == XmlNodeType::StartTag) + { + int depth = 1; + while (depth > 0) + { + node = reader.Read(); + if (node.Type == XmlNodeType::End) + { + throw std::runtime_error("Unexpected end of XML."); + } + if (node.Type == XmlNodeType::StartTag) + { + ++depth; + } + else if (node.Type == XmlNodeType::EndTag) + { + --depth; + } + } + } + } + } + response.QueueItems.Value().emplace_back( + std::move(responseQueueItemsValueItem)); + } + else if (node.Type == XmlNodeType::StartTag) + { + int depth = 1; + while (depth > 0) + { + node = reader.Read(); + if (node.Type == XmlNodeType::End) + { + throw std::runtime_error("Unexpected end of XML."); + } + if (node.Type == XmlNodeType::StartTag) + { + ++depth; + } + else if (node.Type == XmlNodeType::EndTag) + { + --depth; + } + } + } + } + } + else if (node.Type == XmlNodeType::StartTag && node.Name == "NextMarker") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + response.NextMarker = node.Value; + } + } + } + else if (node.Type == XmlNodeType::StartTag) + { + int depth = 1; + while (depth > 0) + { + node = reader.Read(); + if (node.Type == XmlNodeType::End) + { + throw std::runtime_error("Unexpected end of XML."); + } + if (node.Type == XmlNodeType::StartTag) + { + ++depth; + } + else if (node.Type == XmlNodeType::EndTag) + { + --depth; + } + } + } + } + } + } + } + } + catch (Core::RequestFailedException const&) + { + throw; + } + catch (std::exception const& ex) + { + throw Core::RequestFailedException(ex.what()); + } + } + { + auto header = rawResponse->GetHeaders().find("x-ms-version"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.Version = headerValue; + } + else + { + throw std::out_of_range("Missing required response header: x-ms-version"); + } + } + { + auto header = rawResponse->GetHeaders().find("x-ms-request-id"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.RequestId = headerValue; + } + } + { + auto header = rawResponse->GetHeaders().find("x-ms-client-request-id"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.ClientRequestId = headerValue; + } + } + { + auto header = rawResponse->GetHeaders().find("Date"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.Date = DateTime::Parse(headerValue, DateTime::DateFormat::Rfc1123); + } + else + { + throw std::out_of_range("Missing required response header: Date"); + } + } + + return Response(std::move(response), std::move(rawResponse)); +} + +Azure::Response QueuesClient:: + CreateQueue(const QueuesClientCreateQueueOptions& options, const Core::Context& context) const +{ + auto url = m_url; + + if (options.Timeout.HasValue()) + { + url.AppendQueryParameter("timeout", std::to_string(options.Timeout.Value())); + } + + Core::Http::Request request(Core::Http::HttpMethod::Put, url); + + request.SetHeader("x-ms-version", m_xMsVersion); + if (options.XMsClientRequestId.HasValue()) + { + request.SetHeader("x-ms-client-request-id", options.XMsClientRequestId.Value()); + } + + if (options.Metadata.HasValue()) + { + for (auto const& header : options.Metadata.Value()) + { + request.SetHeader(std::string("x-ms-meta-") + header.first, header.second); + } + } + auto rawResponse = m_pipeline->Send(request, context); + const auto httpStatusCode = rawResponse->GetStatusCode(); + + if (httpStatusCode != Core::Http::HttpStatusCode::Created + && httpStatusCode != Core::Http::HttpStatusCode::NoContent) + { + throw Core::RequestFailedException(rawResponse); + } + + Models::CreateQueueResult response{}; + { + auto header = rawResponse->GetHeaders().find("x-ms-version"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.Version = headerValue; + } + else + { + throw std::out_of_range("Missing required response header: x-ms-version"); + } + } + { + auto header = rawResponse->GetHeaders().find("x-ms-request-id"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.RequestId = headerValue; + } + } + { + auto header = rawResponse->GetHeaders().find("x-ms-client-request-id"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.ClientRequestId = headerValue; + } + } + { + auto header = rawResponse->GetHeaders().find("Date"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.Date = DateTime::Parse(headerValue, DateTime::DateFormat::Rfc1123); + } + else + { + throw std::out_of_range("Missing required response header: Date"); + } + } + + return Response(std::move(response), std::move(rawResponse)); +} + +Azure::Response +QueuesClient::GetPropertiesQueue( + const QueuesClientGetPropertiesQueueOptions& options, + const Core::Context& context) const +{ + auto url = m_url; + + url.AppendQueryParameter("comp", "metadata"); + + if (options.Timeout.HasValue()) + { + url.AppendQueryParameter("timeout", std::to_string(options.Timeout.Value())); + } + + Core::Http::Request request(Core::Http::HttpMethod::Get, url); + + request.SetHeader("x-ms-version", m_xMsVersion); + if (options.XMsClientRequestId.HasValue()) + { + request.SetHeader("x-ms-client-request-id", options.XMsClientRequestId.Value()); + } + + auto rawResponse = m_pipeline->Send(request, context); + const auto httpStatusCode = rawResponse->GetStatusCode(); + + if (httpStatusCode != Core::Http::HttpStatusCode::Ok) + { + throw Core::RequestFailedException(rawResponse); + } + + Models::GetPropertiesQueueResult response{}; + { + auto header = rawResponse->GetHeaders().find("x-ms-approximate-messages-count"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + { + std::size_t parsedLength = 0; + auto parsedValue = std::stoll(headerValue, &parsedLength); + if (parsedLength != headerValue.size() + || parsedValue < (std::numeric_limits::min)() + || parsedValue > (std::numeric_limits::max)()) + { + throw std::out_of_range("Invalid integer response header."); + } + response.ApproximateMessagesCount = static_cast(parsedValue); + } + } + } + { + auto header = rawResponse->GetHeaders().find("x-ms-version"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.Version = headerValue; + } + else + { + throw std::out_of_range("Missing required response header: x-ms-version"); + } + } + { + auto header = rawResponse->GetHeaders().find("x-ms-request-id"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.RequestId = headerValue; + } + } + { + auto header = rawResponse->GetHeaders().find("x-ms-client-request-id"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.ClientRequestId = headerValue; + } + } + { + auto header = rawResponse->GetHeaders().find("Date"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.Date = DateTime::Parse(headerValue, DateTime::DateFormat::Rfc1123); + } + else + { + throw std::out_of_range("Missing required response header: Date"); + } + } + for (auto const& header : rawResponse->GetHeaders()) + { + if (header.first.size() > 10 + && Core::_internal::StringExtensions::LocaleInvariantCaseInsensitiveEqual( + header.first.substr(0, 10), "x-ms-meta-")) + { + if (!response.Metadata.HasValue()) + { + response.Metadata = Core::CaseInsensitiveMap{}; + } + response.Metadata.Value()[header.first.substr(10)] = header.second; + } + } + + return Response(std::move(response), std::move(rawResponse)); +} + +Azure::Response QueuesClient:: + DeleteQueue(const QueuesClientDeleteQueueOptions& options, const Core::Context& context) const +{ + auto url = m_url; + + if (options.Timeout.HasValue()) + { + url.AppendQueryParameter("timeout", std::to_string(options.Timeout.Value())); + } + + Core::Http::Request request(Core::Http::HttpMethod::Delete, url); + + request.SetHeader("x-ms-version", m_xMsVersion); + if (options.XMsClientRequestId.HasValue()) + { + request.SetHeader("x-ms-client-request-id", options.XMsClientRequestId.Value()); + } + + auto rawResponse = m_pipeline->Send(request, context); + const auto httpStatusCode = rawResponse->GetStatusCode(); + + if (httpStatusCode != Core::Http::HttpStatusCode::NoContent) + { + throw Core::RequestFailedException(rawResponse); + } + + Models::DeleteQueueResult response{}; + { + auto header = rawResponse->GetHeaders().find("x-ms-version"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.Version = headerValue; + } + else + { + throw std::out_of_range("Missing required response header: x-ms-version"); + } + } + { + auto header = rawResponse->GetHeaders().find("x-ms-request-id"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.RequestId = headerValue; + } + } + { + auto header = rawResponse->GetHeaders().find("x-ms-client-request-id"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.ClientRequestId = headerValue; + } + } + { + auto header = rawResponse->GetHeaders().find("Date"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.Date = DateTime::Parse(headerValue, DateTime::DateFormat::Rfc1123); + } + else + { + throw std::out_of_range("Missing required response header: Date"); + } + } + + return Response(std::move(response), std::move(rawResponse)); +} + +Azure::Response +QueuesClient::SetMetadataQueue( + const QueuesClientSetMetadataQueueOptions& options, + const Core::Context& context) const +{ + auto url = m_url; + + url.AppendQueryParameter("comp", "metadata"); + + if (options.Timeout.HasValue()) + { + url.AppendQueryParameter("timeout", std::to_string(options.Timeout.Value())); + } + + Core::Http::Request request(Core::Http::HttpMethod::Put, url); + + request.SetHeader("x-ms-version", m_xMsVersion); + if (options.XMsClientRequestId.HasValue()) + { + request.SetHeader("x-ms-client-request-id", options.XMsClientRequestId.Value()); + } + + if (options.Metadata.HasValue()) + { + for (auto const& header : options.Metadata.Value()) + { + request.SetHeader(std::string("x-ms-meta-") + header.first, header.second); + } + } + auto rawResponse = m_pipeline->Send(request, context); + const auto httpStatusCode = rawResponse->GetStatusCode(); + + if (httpStatusCode != Core::Http::HttpStatusCode::NoContent) + { + throw Core::RequestFailedException(rawResponse); + } + + Models::SetMetadataQueueResult response{}; + { + auto header = rawResponse->GetHeaders().find("x-ms-version"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.Version = headerValue; + } + else + { + throw std::out_of_range("Missing required response header: x-ms-version"); + } + } + { + auto header = rawResponse->GetHeaders().find("x-ms-request-id"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.RequestId = headerValue; + } + } + { + auto header = rawResponse->GetHeaders().find("x-ms-client-request-id"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.ClientRequestId = headerValue; + } + } + { + auto header = rawResponse->GetHeaders().find("Date"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.Date = DateTime::Parse(headerValue, DateTime::DateFormat::Rfc1123); + } + else + { + throw std::out_of_range("Missing required response header: Date"); + } + } + + return Response(std::move(response), std::move(rawResponse)); +} + +Azure::Response +QueuesClient::GetAccessPolicyQueue( + const QueuesClientGetAccessPolicyQueueOptions& options, + const Core::Context& context) const +{ + auto url = m_url; + + url.AppendQueryParameter("comp", "acl"); + + if (options.Timeout.HasValue()) + { + url.AppendQueryParameter("timeout", std::to_string(options.Timeout.Value())); + } + + Core::Http::Request request(Core::Http::HttpMethod::Get, url); + + request.SetHeader("Accept", "application/xml"); + + request.SetHeader("x-ms-version", m_xMsVersion); + if (options.XMsClientRequestId.HasValue()) + { + request.SetHeader("x-ms-client-request-id", options.XMsClientRequestId.Value()); + } + + auto rawResponse = m_pipeline->Send(request, context); + const auto httpStatusCode = rawResponse->GetStatusCode(); + + if (httpStatusCode != Core::Http::HttpStatusCode::Ok) + { + throw Core::RequestFailedException(rawResponse); + } + + Models::GetAccessPolicyQueueResult response{}; + { + using namespace _internal; + const auto& responseBody = rawResponse->GetBody(); + XmlReader reader(reinterpret_cast(responseBody.data()), responseBody.size()); + try + { + for (auto node = reader.Read(); node.Type != XmlNodeType::End; node = reader.Read()) + { + if (node.Type == XmlNodeType::StartTag && node.Name == "SignedIdentifiers") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::StartTag || node.Type == XmlNodeType::Attribute) + { + if (node.Type == XmlNodeType::StartTag && node.Name == "SignedIdentifier") + { + Models::SignedIdentifier responseItemsItem{}; + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::StartTag || node.Type == XmlNodeType::Attribute) + { + if (node.Type == XmlNodeType::StartTag && node.Name == "Id") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + responseItemsItem.Id = node.Value; + } + } + } + else if (node.Type == XmlNodeType::StartTag && node.Name == "AccessPolicy") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::StartTag + || node.Type == XmlNodeType::Attribute) + { + if (node.Type == XmlNodeType::StartTag && node.Name == "Start") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + responseItemsItem.AccessPolicy.Start + = DateTime::Parse(node.Value, DateTime::DateFormat::Rfc3339); + } + } + } + else if (node.Type == XmlNodeType::StartTag && node.Name == "Expiry") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + responseItemsItem.AccessPolicy.Expiry + = DateTime::Parse(node.Value, DateTime::DateFormat::Rfc3339); + } + } + } + else if (node.Type == XmlNodeType::StartTag && node.Name == "Permission") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + responseItemsItem.AccessPolicy.Permission = node.Value; + } + } + } + else if (node.Type == XmlNodeType::StartTag) + { + int depth = 1; + while (depth > 0) + { + node = reader.Read(); + if (node.Type == XmlNodeType::End) + { + throw std::runtime_error("Unexpected end of XML."); + } + if (node.Type == XmlNodeType::StartTag) + { + ++depth; + } + else if (node.Type == XmlNodeType::EndTag) + { + --depth; + } + } + } + } + } + } + else if (node.Type == XmlNodeType::StartTag) + { + int depth = 1; + while (depth > 0) + { + node = reader.Read(); + if (node.Type == XmlNodeType::End) + { + throw std::runtime_error("Unexpected end of XML."); + } + if (node.Type == XmlNodeType::StartTag) + { + ++depth; + } + else if (node.Type == XmlNodeType::EndTag) + { + --depth; + } + } + } + } + } + response.Items.emplace_back(std::move(responseItemsItem)); + } + else if (node.Type == XmlNodeType::StartTag) + { + int depth = 1; + while (depth > 0) + { + node = reader.Read(); + if (node.Type == XmlNodeType::End) + { + throw std::runtime_error("Unexpected end of XML."); + } + if (node.Type == XmlNodeType::StartTag) + { + ++depth; + } + else if (node.Type == XmlNodeType::EndTag) + { + --depth; + } + } + } + } + } + } + } + } + catch (Core::RequestFailedException const&) + { + throw; + } + catch (std::exception const& ex) + { + throw Core::RequestFailedException(ex.what()); + } + } + { + auto header = rawResponse->GetHeaders().find("x-ms-version"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.Version = headerValue; + } + else + { + throw std::out_of_range("Missing required response header: x-ms-version"); + } + } + { + auto header = rawResponse->GetHeaders().find("x-ms-request-id"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.RequestId = headerValue; + } + } + { + auto header = rawResponse->GetHeaders().find("x-ms-client-request-id"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.ClientRequestId = headerValue; + } + } + { + auto header = rawResponse->GetHeaders().find("Date"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.Date = DateTime::Parse(headerValue, DateTime::DateFormat::Rfc1123); + } + else + { + throw std::out_of_range("Missing required response header: Date"); + } + } + + return Response(std::move(response), std::move(rawResponse)); +} + +Azure::Response +QueuesClient::SetAccessPolicyQueue( + const QueuesClientSetAccessPolicyQueueOptions& options, + const Core::Context& context) const +{ + auto url = m_url; + + url.AppendQueryParameter("comp", "acl"); + + if (options.Timeout.HasValue()) + { + url.AppendQueryParameter("timeout", std::to_string(options.Timeout.Value())); + } + + std::string xmlBody; + if (options.QueueAcl.HasValue()) + { + using namespace _internal; + XmlWriter writer; + + if (options.QueueAcl.HasValue()) + { + writer.Write(XmlNode{XmlNodeType::StartTag, "SignedIdentifiers"}); + for (auto const& item : options.QueueAcl.Value().Items) + { + writer.Write(XmlNode{XmlNodeType::StartTag, "SignedIdentifier"}); + writer.Write(XmlNode{XmlNodeType::StartTag, "Id", item.Id}); + writer.Write(XmlNode{XmlNodeType::StartTag, "AccessPolicy"}); + if (item.AccessPolicy.Start.HasValue()) + { + writer.Write(XmlNode{ + XmlNodeType::StartTag, + "Start", + item.AccessPolicy.Start.Value().ToString( + DateTime::DateFormat::Rfc3339, DateTime::TimeFractionFormat::AllDigits)}); + } + if (item.AccessPolicy.Expiry.HasValue()) + { + writer.Write(XmlNode{ + XmlNodeType::StartTag, + "Expiry", + item.AccessPolicy.Expiry.Value().ToString( + DateTime::DateFormat::Rfc3339, DateTime::TimeFractionFormat::AllDigits)}); + } + if (item.AccessPolicy.Permission.HasValue()) + { + writer.Write( + XmlNode{XmlNodeType::StartTag, "Permission", item.AccessPolicy.Permission.Value()}); + } + writer.Write(XmlNode{XmlNodeType::EndTag}); + writer.Write(XmlNode{XmlNodeType::EndTag}); + } + writer.Write(XmlNode{XmlNodeType::EndTag}); + } + writer.Write(XmlNode{XmlNodeType::End}); + xmlBody = writer.GetDocument(); + } + + Core::IO::MemoryBodyStream requestBody( + reinterpret_cast(xmlBody.data()), xmlBody.length()); + + Core::Http::Request request(Core::Http::HttpMethod::Put, url, &requestBody); + + request.SetHeader("x-ms-version", m_xMsVersion); + if (options.XMsClientRequestId.HasValue()) + { + request.SetHeader("x-ms-client-request-id", options.XMsClientRequestId.Value()); + } + + if (options.QueueAcl.HasValue()) + { + request.SetHeader("Content-Type", "application/xml"); + } + request.SetHeader("Content-Length", std::to_string(requestBody.Length())); + + auto rawResponse = m_pipeline->Send(request, context); + const auto httpStatusCode = rawResponse->GetStatusCode(); + + if (httpStatusCode != Core::Http::HttpStatusCode::NoContent) + { + throw Core::RequestFailedException(rawResponse); + } + + Models::SetAccessPolicyQueueResult response{}; + { + auto header = rawResponse->GetHeaders().find("x-ms-version"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.Version = headerValue; + } + else + { + throw std::out_of_range("Missing required response header: x-ms-version"); + } + } + { + auto header = rawResponse->GetHeaders().find("x-ms-request-id"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.RequestId = headerValue; + } + } + { + auto header = rawResponse->GetHeaders().find("x-ms-client-request-id"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.ClientRequestId = headerValue; + } + } + { + auto header = rawResponse->GetHeaders().find("Date"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.Date = DateTime::Parse(headerValue, DateTime::DateFormat::Rfc1123); + } + else + { + throw std::out_of_range("Missing required response header: Date"); + } + } + + return Response(std::move(response), std::move(rawResponse)); +} + +Azure::Response +QueuesClient::ReceiveMessagesQueue( + const QueuesClientReceiveMessagesQueueOptions& options, + const Core::Context& context) const +{ + auto url = m_url; + url.AppendPath("messages"); + + if (options.Numofmessages.HasValue()) + { + url.AppendQueryParameter("numofmessages", std::to_string(options.Numofmessages.Value())); + } + + if (options.Visibilitytimeout.HasValue()) + { + url.AppendQueryParameter( + "visibilitytimeout", std::to_string(options.Visibilitytimeout.Value())); + } + + if (options.Timeout.HasValue()) + { + url.AppendQueryParameter("timeout", std::to_string(options.Timeout.Value())); + } + + Core::Http::Request request(Core::Http::HttpMethod::Get, url); + + request.SetHeader("Accept", "application/xml"); + + request.SetHeader("x-ms-version", m_xMsVersion); + if (options.XMsClientRequestId.HasValue()) + { + request.SetHeader("x-ms-client-request-id", options.XMsClientRequestId.Value()); + } + + auto rawResponse = m_pipeline->Send(request, context); + const auto httpStatusCode = rawResponse->GetStatusCode(); + + if (httpStatusCode != Core::Http::HttpStatusCode::Ok) + { + throw Core::RequestFailedException(rawResponse); + } + + Models::ReceiveMessagesQueueResult response{}; + { + using namespace _internal; + const auto& responseBody = rawResponse->GetBody(); + XmlReader reader(reinterpret_cast(responseBody.data()), responseBody.size()); + try + { + for (auto node = reader.Read(); node.Type != XmlNodeType::End; node = reader.Read()) + { + if (node.Type == XmlNodeType::StartTag && node.Name == "QueueMessagesList") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::StartTag || node.Type == XmlNodeType::Attribute) + { + if (node.Type == XmlNodeType::StartTag && node.Name == "QueueMessage") + { + Models::ReceivedMessage responseItemsItem{}; + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::StartTag || node.Type == XmlNodeType::Attribute) + { + if (node.Type == XmlNodeType::StartTag && node.Name == "MessageId") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + responseItemsItem.MessageId = node.Value; + } + } + } + else if (node.Type == XmlNodeType::StartTag && node.Name == "InsertionTime") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + responseItemsItem.InsertionTime + = DateTime::Parse(node.Value, DateTime::DateFormat::Rfc1123); + } + } + } + else if (node.Type == XmlNodeType::StartTag && node.Name == "ExpirationTime") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + responseItemsItem.ExpirationTime + = DateTime::Parse(node.Value, DateTime::DateFormat::Rfc1123); + } + } + } + else if (node.Type == XmlNodeType::StartTag && node.Name == "PopReceipt") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + responseItemsItem.PopReceipt = node.Value; + } + } + } + else if (node.Type == XmlNodeType::StartTag && node.Name == "TimeNextVisible") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + responseItemsItem.TimeNextVisible + = DateTime::Parse(node.Value, DateTime::DateFormat::Rfc1123); + } + } + } + else if (node.Type == XmlNodeType::StartTag && node.Name == "DequeueCount") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + responseItemsItem.DequeueCount = std::stoll(node.Value); + } + } + } + else if (node.Type == XmlNodeType::StartTag && node.Name == "MessageText") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + responseItemsItem.MessageText = node.Value; + } + } + } + else if (node.Type == XmlNodeType::StartTag) + { + int depth = 1; + while (depth > 0) + { + node = reader.Read(); + if (node.Type == XmlNodeType::End) + { + throw std::runtime_error("Unexpected end of XML."); + } + if (node.Type == XmlNodeType::StartTag) + { + ++depth; + } + else if (node.Type == XmlNodeType::EndTag) + { + --depth; + } + } + } + } + } + response.Items.emplace_back(std::move(responseItemsItem)); + } + else if (node.Type == XmlNodeType::StartTag) + { + int depth = 1; + while (depth > 0) + { + node = reader.Read(); + if (node.Type == XmlNodeType::End) + { + throw std::runtime_error("Unexpected end of XML."); + } + if (node.Type == XmlNodeType::StartTag) + { + ++depth; + } + else if (node.Type == XmlNodeType::EndTag) + { + --depth; + } + } + } + } + } + } + } + } + catch (Core::RequestFailedException const&) + { + throw; + } + catch (std::exception const& ex) + { + throw Core::RequestFailedException(ex.what()); + } + } + { + auto header = rawResponse->GetHeaders().find("x-ms-version"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.Version = headerValue; + } + else + { + throw std::out_of_range("Missing required response header: x-ms-version"); + } + } + { + auto header = rawResponse->GetHeaders().find("x-ms-request-id"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.RequestId = headerValue; + } + } + { + auto header = rawResponse->GetHeaders().find("x-ms-client-request-id"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.ClientRequestId = headerValue; + } + } + { + auto header = rawResponse->GetHeaders().find("Date"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.Date = DateTime::Parse(headerValue, DateTime::DateFormat::Rfc1123); + } + else + { + throw std::out_of_range("Missing required response header: Date"); + } + } + + return Response(std::move(response), std::move(rawResponse)); +} + +Azure::Response QueuesClient::ClearQueue( + const QueuesClientClearQueueOptions& options, + const Core::Context& context) const +{ + auto url = m_url; + url.AppendPath("messages"); + + if (options.Timeout.HasValue()) + { + url.AppendQueryParameter("timeout", std::to_string(options.Timeout.Value())); + } + + Core::Http::Request request(Core::Http::HttpMethod::Delete, url); + + request.SetHeader("x-ms-version", m_xMsVersion); + if (options.XMsClientRequestId.HasValue()) + { + request.SetHeader("x-ms-client-request-id", options.XMsClientRequestId.Value()); + } + + auto rawResponse = m_pipeline->Send(request, context); + const auto httpStatusCode = rawResponse->GetStatusCode(); + + if (httpStatusCode != Core::Http::HttpStatusCode::NoContent) + { + throw Core::RequestFailedException(rawResponse); + } + + Models::ClearQueueResult response{}; + { + auto header = rawResponse->GetHeaders().find("x-ms-version"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.Version = headerValue; + } + else + { + throw std::out_of_range("Missing required response header: x-ms-version"); + } + } + { + auto header = rawResponse->GetHeaders().find("x-ms-request-id"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.RequestId = headerValue; + } + } + { + auto header = rawResponse->GetHeaders().find("x-ms-client-request-id"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.ClientRequestId = headerValue; + } + } + { + auto header = rawResponse->GetHeaders().find("Date"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.Date = DateTime::Parse(headerValue, DateTime::DateFormat::Rfc1123); + } + else + { + throw std::out_of_range("Missing required response header: Date"); + } + } + + return Response(std::move(response), std::move(rawResponse)); +} + +Azure::Response +QueuesClient::SendMessageQueue( + const Models::QueueMessage& queueMessage, + const QueuesClientSendMessageQueueOptions& options, + const Core::Context& context) const +{ + auto url = m_url; + url.AppendPath("messages"); + + if (options.Visibilitytimeout.HasValue()) + { + url.AppendQueryParameter( + "visibilitytimeout", std::to_string(options.Visibilitytimeout.Value())); + } + + if (options.Messagettl.HasValue()) + { + url.AppendQueryParameter("messagettl", std::to_string(options.Messagettl.Value())); + } + + if (options.Timeout.HasValue()) + { + url.AppendQueryParameter("timeout", std::to_string(options.Timeout.Value())); + } + + std::string xmlBody; + { + using namespace _internal; + XmlWriter writer; + + writer.Write(XmlNode{XmlNodeType::StartTag, "QueueMessage"}); + writer.Write(XmlNode{XmlNodeType::StartTag, "MessageText", queueMessage.MessageText}); + writer.Write(XmlNode{XmlNodeType::EndTag}); + + writer.Write(XmlNode{XmlNodeType::End}); + xmlBody = writer.GetDocument(); + } + + Core::IO::MemoryBodyStream requestBody( + reinterpret_cast(xmlBody.data()), xmlBody.length()); + + Core::Http::Request request(Core::Http::HttpMethod::Post, url, &requestBody); + + request.SetHeader("Content-Type", "application/xml"); + request.SetHeader("Accept", "application/xml"); + + request.SetHeader("x-ms-version", m_xMsVersion); + if (options.XMsClientRequestId.HasValue()) + { + request.SetHeader("x-ms-client-request-id", options.XMsClientRequestId.Value()); + } + + request.SetHeader("Content-Length", std::to_string(requestBody.Length())); + + auto rawResponse = m_pipeline->Send(request, context); + const auto httpStatusCode = rawResponse->GetStatusCode(); + + if (httpStatusCode != Core::Http::HttpStatusCode::Created) + { + throw Core::RequestFailedException(rawResponse); + } + + Models::SendMessageQueueResult response{}; + { + using namespace _internal; + const auto& responseBody = rawResponse->GetBody(); + XmlReader reader(reinterpret_cast(responseBody.data()), responseBody.size()); + try + { + for (auto node = reader.Read(); node.Type != XmlNodeType::End; node = reader.Read()) + { + if (node.Type == XmlNodeType::StartTag && node.Name == "QueueMessagesList") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::StartTag || node.Type == XmlNodeType::Attribute) + { + if (node.Type == XmlNodeType::StartTag && node.Name == "QueueMessage") + { + Models::SentMessage responseItemsItem{}; + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::StartTag || node.Type == XmlNodeType::Attribute) + { + if (node.Type == XmlNodeType::StartTag && node.Name == "MessageId") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + responseItemsItem.MessageId = node.Value; + } + } + } + else if (node.Type == XmlNodeType::StartTag && node.Name == "InsertionTime") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + responseItemsItem.InsertionTime + = DateTime::Parse(node.Value, DateTime::DateFormat::Rfc1123); + } + } + } + else if (node.Type == XmlNodeType::StartTag && node.Name == "ExpirationTime") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + responseItemsItem.ExpirationTime + = DateTime::Parse(node.Value, DateTime::DateFormat::Rfc1123); + } + } + } + else if (node.Type == XmlNodeType::StartTag && node.Name == "PopReceipt") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + responseItemsItem.PopReceipt = node.Value; + } + } + } + else if (node.Type == XmlNodeType::StartTag && node.Name == "TimeNextVisible") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + responseItemsItem.TimeNextVisible + = DateTime::Parse(node.Value, DateTime::DateFormat::Rfc1123); + } + } + } + else if (node.Type == XmlNodeType::StartTag) + { + int depth = 1; + while (depth > 0) + { + node = reader.Read(); + if (node.Type == XmlNodeType::End) + { + throw std::runtime_error("Unexpected end of XML."); + } + if (node.Type == XmlNodeType::StartTag) + { + ++depth; + } + else if (node.Type == XmlNodeType::EndTag) + { + --depth; + } + } + } + } + } + response.Items.emplace_back(std::move(responseItemsItem)); + } + else if (node.Type == XmlNodeType::StartTag) + { + int depth = 1; + while (depth > 0) + { + node = reader.Read(); + if (node.Type == XmlNodeType::End) + { + throw std::runtime_error("Unexpected end of XML."); + } + if (node.Type == XmlNodeType::StartTag) + { + ++depth; + } + else if (node.Type == XmlNodeType::EndTag) + { + --depth; + } + } + } + } + } + } + } + } + catch (Core::RequestFailedException const&) + { + throw; + } + catch (std::exception const& ex) + { + throw Core::RequestFailedException(ex.what()); + } + } + { + auto header = rawResponse->GetHeaders().find("x-ms-version"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.Version = headerValue; + } + else + { + throw std::out_of_range("Missing required response header: x-ms-version"); + } + } + { + auto header = rawResponse->GetHeaders().find("x-ms-request-id"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.RequestId = headerValue; + } + } + { + auto header = rawResponse->GetHeaders().find("x-ms-client-request-id"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.ClientRequestId = headerValue; + } + } + { + auto header = rawResponse->GetHeaders().find("Date"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.Date = DateTime::Parse(headerValue, DateTime::DateFormat::Rfc1123); + } + else + { + throw std::out_of_range("Missing required response header: Date"); + } + } + + return Response(std::move(response), std::move(rawResponse)); +} + +Azure::Response +QueuesClient::PeekMessagesQueue( + const QueuesClientPeekMessagesQueueOptions& options, + const Core::Context& context) const +{ + auto url = m_url; + url.AppendPath("messages"); + + url.AppendQueryParameter("peekonly", "true"); + + if (options.Numofmessages.HasValue()) + { + url.AppendQueryParameter("numofmessages", std::to_string(options.Numofmessages.Value())); + } + + if (options.Timeout.HasValue()) + { + url.AppendQueryParameter("timeout", std::to_string(options.Timeout.Value())); + } + + Core::Http::Request request(Core::Http::HttpMethod::Get, url); + + request.SetHeader("Accept", "application/xml"); + + request.SetHeader("x-ms-version", m_xMsVersion); + if (options.XMsClientRequestId.HasValue()) + { + request.SetHeader("x-ms-client-request-id", options.XMsClientRequestId.Value()); + } + + auto rawResponse = m_pipeline->Send(request, context); + const auto httpStatusCode = rawResponse->GetStatusCode(); + + if (httpStatusCode != Core::Http::HttpStatusCode::Ok) + { + throw Core::RequestFailedException(rawResponse); + } + + Models::PeekMessagesQueueResult response{}; + { + using namespace _internal; + const auto& responseBody = rawResponse->GetBody(); + XmlReader reader(reinterpret_cast(responseBody.data()), responseBody.size()); + try + { + for (auto node = reader.Read(); node.Type != XmlNodeType::End; node = reader.Read()) + { + if (node.Type == XmlNodeType::StartTag && node.Name == "QueueMessagesList") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::StartTag || node.Type == XmlNodeType::Attribute) + { + if (node.Type == XmlNodeType::StartTag && node.Name == "QueueMessage") + { + Models::PeekedMessage responseItemsItem{}; + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::StartTag || node.Type == XmlNodeType::Attribute) + { + if (node.Type == XmlNodeType::StartTag && node.Name == "MessageId") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + responseItemsItem.MessageId = node.Value; + } + } + } + else if (node.Type == XmlNodeType::StartTag && node.Name == "InsertionTime") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + responseItemsItem.InsertionTime + = DateTime::Parse(node.Value, DateTime::DateFormat::Rfc1123); + } + } + } + else if (node.Type == XmlNodeType::StartTag && node.Name == "ExpirationTime") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + responseItemsItem.ExpirationTime + = DateTime::Parse(node.Value, DateTime::DateFormat::Rfc1123); + } + } + } + else if (node.Type == XmlNodeType::StartTag && node.Name == "DequeueCount") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + responseItemsItem.DequeueCount = std::stoll(node.Value); + } + } + } + else if (node.Type == XmlNodeType::StartTag && node.Name == "MessageText") + { + for (node = reader.Read(); + node.Type != XmlNodeType::EndTag && node.Type != XmlNodeType::End; + node = reader.Read()) + { + if (node.Type == XmlNodeType::Text) + { + responseItemsItem.MessageText = node.Value; + } + } + } + else if (node.Type == XmlNodeType::StartTag) + { + int depth = 1; + while (depth > 0) + { + node = reader.Read(); + if (node.Type == XmlNodeType::End) + { + throw std::runtime_error("Unexpected end of XML."); + } + if (node.Type == XmlNodeType::StartTag) + { + ++depth; + } + else if (node.Type == XmlNodeType::EndTag) + { + --depth; + } + } + } + } + } + response.Items.emplace_back(std::move(responseItemsItem)); + } + else if (node.Type == XmlNodeType::StartTag) + { + int depth = 1; + while (depth > 0) + { + node = reader.Read(); + if (node.Type == XmlNodeType::End) + { + throw std::runtime_error("Unexpected end of XML."); + } + if (node.Type == XmlNodeType::StartTag) + { + ++depth; + } + else if (node.Type == XmlNodeType::EndTag) + { + --depth; + } + } + } + } + } + } + } + } + catch (Core::RequestFailedException const&) + { + throw; + } + catch (std::exception const& ex) + { + throw Core::RequestFailedException(ex.what()); + } + } + { + auto header = rawResponse->GetHeaders().find("x-ms-version"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.Version = headerValue; + } + else + { + throw std::out_of_range("Missing required response header: x-ms-version"); + } + } + { + auto header = rawResponse->GetHeaders().find("x-ms-request-id"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.RequestId = headerValue; + } + } + { + auto header = rawResponse->GetHeaders().find("x-ms-client-request-id"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.ClientRequestId = headerValue; + } + } + { + auto header = rawResponse->GetHeaders().find("Date"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.Date = DateTime::Parse(headerValue, DateTime::DateFormat::Rfc1123); + } + else + { + throw std::out_of_range("Missing required response header: Date"); + } + } + + return Response(std::move(response), std::move(rawResponse)); +} + +Azure::Response +QueuesClient::UpdateMessageQueue( + const std::string& messageId, + const std::string& popreceipt, + std::int32_t visibilitytimeout, + const QueuesClientUpdateMessageQueueOptions& options, + const Core::Context& context) const +{ + auto url = m_url; + url.AppendPath("messages"); + if (messageId.empty()) + { + throw std::invalid_argument("Parameter 'messageId' cannot be an empty string."); + } + url.AppendPath(Core::Url::Encode(messageId)); + + url.AppendQueryParameter("popreceipt", Core::Url::Encode(popreceipt)); + url.AppendQueryParameter("visibilitytimeout", std::to_string(visibilitytimeout)); + if (options.Timeout.HasValue()) + { + url.AppendQueryParameter("timeout", std::to_string(options.Timeout.Value())); + } + + std::string xmlBody; + if (options.QueueMessage.HasValue()) + { + using namespace _internal; + XmlWriter writer; + + if (options.QueueMessage.HasValue()) + { + writer.Write(XmlNode{XmlNodeType::StartTag, "QueueMessage"}); + writer.Write( + XmlNode{XmlNodeType::StartTag, "MessageText", options.QueueMessage.Value().MessageText}); + writer.Write(XmlNode{XmlNodeType::EndTag}); + } + writer.Write(XmlNode{XmlNodeType::End}); + xmlBody = writer.GetDocument(); + } + + Core::IO::MemoryBodyStream requestBody( + reinterpret_cast(xmlBody.data()), xmlBody.length()); + + Core::Http::Request request(Core::Http::HttpMethod::Put, url, &requestBody); + + request.SetHeader("x-ms-version", m_xMsVersion); + if (options.XMsClientRequestId.HasValue()) + { + request.SetHeader("x-ms-client-request-id", options.XMsClientRequestId.Value()); + } + + if (options.QueueMessage.HasValue()) + { + request.SetHeader("Content-Type", "application/xml"); + } + request.SetHeader("Content-Length", std::to_string(requestBody.Length())); + + auto rawResponse = m_pipeline->Send(request, context); + const auto httpStatusCode = rawResponse->GetStatusCode(); + + if (httpStatusCode != Core::Http::HttpStatusCode::NoContent) + { + throw Core::RequestFailedException(rawResponse); + } + + Models::UpdateMessageQueueResult response{}; + { + auto header = rawResponse->GetHeaders().find("x-ms-popreceipt"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.PopReceipt = headerValue; + } + else + { + throw std::out_of_range("Missing required response header: x-ms-popreceipt"); + } + } + { + auto header = rawResponse->GetHeaders().find("x-ms-time-next-visible"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.TimeNextVisible = DateTime::Parse(headerValue, DateTime::DateFormat::Rfc1123); + } + else + { + throw std::out_of_range("Missing required response header: x-ms-time-next-visible"); + } + } + { + auto header = rawResponse->GetHeaders().find("x-ms-version"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.Version = headerValue; + } + else + { + throw std::out_of_range("Missing required response header: x-ms-version"); + } + } + { + auto header = rawResponse->GetHeaders().find("x-ms-request-id"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.RequestId = headerValue; + } + } + { + auto header = rawResponse->GetHeaders().find("x-ms-client-request-id"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.ClientRequestId = headerValue; + } + } + { + auto header = rawResponse->GetHeaders().find("Date"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.Date = DateTime::Parse(headerValue, DateTime::DateFormat::Rfc1123); + } + else + { + throw std::out_of_range("Missing required response header: Date"); + } + } + + return Response(std::move(response), std::move(rawResponse)); +} + +Azure::Response +QueuesClient::DeleteMessageQueue( + const std::string& messageId, + const std::string& popreceipt, + const QueuesClientDeleteMessageQueueOptions& options, + const Core::Context& context) const +{ + auto url = m_url; + url.AppendPath("messages"); + if (messageId.empty()) + { + throw std::invalid_argument("Parameter 'messageId' cannot be an empty string."); + } + url.AppendPath(Core::Url::Encode(messageId)); + + url.AppendQueryParameter("popreceipt", Core::Url::Encode(popreceipt)); + if (options.Timeout.HasValue()) + { + url.AppendQueryParameter("timeout", std::to_string(options.Timeout.Value())); + } + + Core::Http::Request request(Core::Http::HttpMethod::Delete, url); + + request.SetHeader("x-ms-version", m_xMsVersion); + if (options.XMsClientRequestId.HasValue()) + { + request.SetHeader("x-ms-client-request-id", options.XMsClientRequestId.Value()); + } + + auto rawResponse = m_pipeline->Send(request, context); + const auto httpStatusCode = rawResponse->GetStatusCode(); + + if (httpStatusCode != Core::Http::HttpStatusCode::NoContent) + { + throw Core::RequestFailedException(rawResponse); + } + + Models::DeleteMessageQueueResult response{}; + { + auto header = rawResponse->GetHeaders().find("x-ms-version"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.Version = headerValue; + } + else + { + throw std::out_of_range("Missing required response header: x-ms-version"); + } + } + { + auto header = rawResponse->GetHeaders().find("x-ms-request-id"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.RequestId = headerValue; + } + } + { + auto header = rawResponse->GetHeaders().find("x-ms-client-request-id"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.ClientRequestId = headerValue; + } + } + { + auto header = rawResponse->GetHeaders().find("Date"); + if (header != rawResponse->GetHeaders().end()) + { + auto const& headerValue = header->second; + response.Date = DateTime::Parse(headerValue, DateTime::DateFormat::Rfc1123); + } + else + { + throw std::out_of_range("Missing required response header: Date"); + } + } + + return Response(std::move(response), std::move(rawResponse)); +} diff --git a/sdk/storage/azure-storage-queues/src/generated/queues_client.hpp b/sdk/storage/azure-storage-queues/src/generated/queues_client.hpp new file mode 100644 index 0000000000..2878749cbd --- /dev/null +++ b/sdk/storage/azure-storage-queues/src/generated/queues_client.hpp @@ -0,0 +1,276 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// Code generated by Microsoft (R) TypeSpec Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +#pragma once + +#include "queues_client_options.hpp" +#include "queues_models.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace Azure { namespace Storage { namespace Queues { namespace _detail { + /** + * @brief + * + */ + class QueuesClient final { + public: + /** + * @brief Constructs the QueuesClient. + * @param endpoint The URL address where the client will send the requests to. + * @param pipeline Existing HTTP pipeline; policies are not recreated. + * @param options Optional parameters. + * + */ + explicit QueuesClient( + Core::Url endpoint, + std::shared_ptr pipeline, + const QueuesClientOptions& options = {}); + + /** + * @brief Gets the QueuesClient URL endpoint. + * @return The QueuesClient's URL endpoint. + * + */ + std::string GetUrl() const; + + /** + * @brief Sets properties for a storage account's Queue service endpoint, including properties + * for Storage Analytics and CORS (Cross-Origin Resource Sharing) rules. + * @param queueServiceProperties The storage service properties to set. + * @param options Optional parameters. + * @param context The context for the operation can be used for request cancellation. + * @return Operation result. + * + */ + Response SetPropertiesService( + const Models::QueueServiceProperties& queueServiceProperties, + const QueuesClientSetPropertiesServiceOptions& options = {}, + const Core::Context& context = {}) const; + + /** + * @brief Retrieves properties of a storage account's Queue service, including properties for + * Storage Analytics and CORS (Cross-Origin Resource Sharing) rules. + * @param options Optional parameters. + * @param context The context for the operation can be used for request cancellation. + * @return The service properties. + * + */ + Response GetPropertiesService( + const QueuesClientGetPropertiesServiceOptions& options = {}, + const Core::Context& context = {}) const; + + /** + * @brief Retrieves statistics related to replication for the Queue service. It is only + * available on the secondary location endpoint when read-access geo-redundant replication is + * enabled for the storage account. + * @param options Optional parameters. + * @param context The context for the operation can be used for request cancellation. + * @return Statistics returned to the C++ queue protocol client. + * + */ + Response GetStatisticsService( + const QueuesClientGetStatisticsServiceOptions& options = {}, + const Core::Context& context = {}) const; + + /** + * @brief Retrieves a user delegation key for the Queue service. This is only a valid operation + * when using bearer token authentication. + * @param keyInfo Key information. + * @param options Optional parameters. + * @param context The context for the operation can be used for request cancellation. + * @return A user delegation key. + * + */ + Response GetUserDelegationKeyService( + const Models::KeyInfo& keyInfo, + const QueuesClientGetUserDelegationKeyServiceOptions& options = {}, + const Core::Context& context = {}) const; + + /** + * @brief Returns a list of queues. + * @param options Optional parameters. + * @param context The context for the operation can be used for request cancellation. + * @return The list queues response. + * + */ + Response GetQueuesService( + const QueuesClientGetQueuesServiceOptions& options = {}, + const Core::Context& context = {}) const; + + /** + * @brief Creates a new queue. If a queue with the same name already exists, the operation + * succeeds when the metadata is identical. If the metadata differs, the operation fails. + * @param options Optional parameters. + * @param context The context for the operation can be used for request cancellation. + * @return Operation result. + * + */ + Response CreateQueue( + const QueuesClientCreateQueueOptions& options = {}, + const Core::Context& context = {}) const; + + /** + * @brief Returns all user-defined metadata and system properties for the specified queue. + * @param options Optional parameters. + * @param context The context for the operation can be used for request cancellation. + * @return Operation result. + * + */ + Response GetPropertiesQueue( + const QueuesClientGetPropertiesQueueOptions& options = {}, + const Core::Context& context = {}) const; + + /** + * @brief Permanently deletes the specified queue. + * @param options Optional parameters. + * @param context The context for the operation can be used for request cancellation. + * @return Operation result. + * + */ + Response DeleteQueue( + const QueuesClientDeleteQueueOptions& options = {}, + const Core::Context& context = {}) const; + + /** + * @brief Sets user-defined metadata for the specified queue. + * @param options Optional parameters. + * @param context The context for the operation can be used for request cancellation. + * @return Operation result. + * + */ + Response SetMetadataQueue( + const QueuesClientSetMetadataQueueOptions& options = {}, + const Core::Context& context = {}) const; + + /** + * @brief Gets the access policy for the specified queue. + * @param options Optional parameters. + * @param context The context for the operation can be used for request cancellation. + * @return An array of signed identifiers. + * + */ + Response GetAccessPolicyQueue( + const QueuesClientGetAccessPolicyQueueOptions& options = {}, + const Core::Context& context = {}) const; + + /** + * @brief Sets the permissions for the specified queue. + * @param options Optional parameters. + * @param context The context for the operation can be used for request cancellation. + * @return Operation result. + * + */ + Response SetAccessPolicyQueue( + const QueuesClientSetAccessPolicyQueueOptions& options = {}, + const Core::Context& context = {}) const; + + /** + * @brief Retrieves one or more messages from the front of the queue. + * @param options Optional parameters. + * @param context The context for the operation can be used for request cancellation. + * @return The response of receive messages. + * + */ + Response ReceiveMessagesQueue( + const QueuesClientReceiveMessagesQueueOptions& options = {}, + const Core::Context& context = {}) const; + + /** + * @brief Deletes all messages from the specified queue. + * @param options Optional parameters. + * @param context The context for the operation can be used for request cancellation. + * @return Operation result. + * + */ + Response ClearQueue( + const QueuesClientClearQueueOptions& options = {}, + const Core::Context& context = {}) const; + + /** + * @brief Adds a new message to the back of the message queue. A visibility timeout can also be + * specified to make the message invisible until the visibility timeout expires. + * @param queueMessage The queue message. The message must be in a format that can be included + * in an XML request with UTF-8 encoding. The encoded message can be up to 64 KB in size. + * @param options Optional parameters. + * @param context The context for the operation can be used for request cancellation. + * @return The response of send message. + * + */ + Response SendMessageQueue( + const Models::QueueMessage& queueMessage, + const QueuesClientSendMessageQueueOptions& options = {}, + const Core::Context& context = {}) const; + + /** + * @brief Retrieves one or more messages from the front of the queue, but does not alter the + * visibility of the message. + * @param options Optional parameters. + * @param context The context for the operation can be used for request cancellation. + * @return The response of peek messages. + * + */ + Response PeekMessagesQueue( + const QueuesClientPeekMessagesQueueOptions& options = {}, + const Core::Context& context = {}) const; + + /** + * @brief Updates the visibility timeout of a message. This operation can also be used to update + * the contents of a message. + * @param messageId The ID of the queue message. + * @param popreceipt An opaque value required to delete the message. If deletion fails using + * this PopReceipt then the message has been dequeued by another client. + * @param visibilitytimeout Specifies the new visibility timeout value, in seconds, relative to + * server time. A specified value must be larger than or equal to 1 second, and cannot be larger + * than 7 days. The visibility timeout of a message can be set to a value later than the expiry + * time. + * @param options Optional parameters. + * @param context The context for the operation can be used for request cancellation. + * @return Operation result. + * + */ + Response UpdateMessageQueue( + const std::string& messageId, + const std::string& popreceipt, + std::int32_t visibilitytimeout, + const QueuesClientUpdateMessageQueueOptions& options = {}, + const Core::Context& context = {}) const; + + /** + * @brief Deletes the specified message. + * @param messageId The ID of the queue message. + * @param popreceipt An opaque value required to delete the message. If deletion fails using + * this PopReceipt then the message has been dequeued by another client. + * @param options Optional parameters. + * @param context The context for the operation can be used for request cancellation. + * @return Operation result. + * + */ + Response DeleteMessageQueue( + const std::string& messageId, + const std::string& popreceipt, + const QueuesClientDeleteMessageQueueOptions& options = {}, + const Core::Context& context = {}) const; + + private: + std::shared_ptr m_pipeline; + Core::Url m_url; + std::string m_xMsVersion; + }; +}}}} // namespace Azure::Storage::Queues::_detail diff --git a/sdk/storage/azure-storage-queues/src/generated/queues_client_options.hpp b/sdk/storage/azure-storage-queues/src/generated/queues_client_options.hpp new file mode 100644 index 0000000000..d41f8e593e --- /dev/null +++ b/sdk/storage/azure-storage-queues/src/generated/queues_client_options.hpp @@ -0,0 +1,344 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// Code generated by Microsoft (R) TypeSpec Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +#pragma once + +#include "queues_models.hpp" + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace Azure { namespace Storage { namespace Queues { namespace _detail { + /** + * @brief QueuesClient options. + * + */ + struct QueuesClientOptions final + { + /// Specifies the version of the operation to use for this request. + std::string XMsVersion = "2026-04-06"; + }; + + /** + * @brief SetPropertiesService operation options. + * + */ + struct QueuesClientSetPropertiesServiceOptions final + { + /// An opaque, globally-unique, client-generated string identifier for the request. + Nullable XMsClientRequestId; + + /// The timeout parameter is expressed in seconds. For more information, see Setting + /// Timeouts for Queue Service Operations. + Nullable Timeout; + }; + + /** + * @brief GetPropertiesService operation options. + * + */ + struct QueuesClientGetPropertiesServiceOptions final + { + /// An opaque, globally-unique, client-generated string identifier for the request. + Nullable XMsClientRequestId; + + /// The timeout parameter is expressed in seconds. For more information, see Setting + /// Timeouts for Queue Service Operations. + Nullable Timeout; + }; + + /** + * @brief GetStatisticsService operation options. + * + */ + struct QueuesClientGetStatisticsServiceOptions final + { + /// An opaque, globally-unique, client-generated string identifier for the request. + Nullable XMsClientRequestId; + + /// The timeout parameter is expressed in seconds. For more information, see Setting + /// Timeouts for Queue Service Operations. + Nullable Timeout; + }; + + /** + * @brief GetUserDelegationKeyService operation options. + * + */ + struct QueuesClientGetUserDelegationKeyServiceOptions final + { + /// An opaque, globally-unique, client-generated string identifier for the request. + Nullable XMsClientRequestId; + + /// The timeout parameter is expressed in seconds. For more information, see Setting + /// Timeouts for Queue Service Operations. + Nullable Timeout; + }; + + /** + * @brief GetQueuesService operation options. + * + */ + struct QueuesClientGetQueuesServiceOptions final + { + /// The URL to fetch the next page of results. + std::string NextPageToken; + + /// An opaque, globally-unique, client-generated string identifier for the request. + Nullable XMsClientRequestId; + + /// Filters the results to return only queues whose name begins with the specified prefix. + Nullable Prefix; + + /// Identifies the portion of the list of queues to be returned with the next listing operation. + /// The operation returns the marker value if the listing operation did not return all queues + /// remaining. The marker value can be used as the value for the marker parameter in a + /// subsequent call to request the next page of list items. The marker value is opaque to the + /// client. + Nullable Marker; + + /// Specifies the maximum number of queues to return. If the request does not specify + /// maxresults, or specifies a value greater than 5000, the server will return up to 5000 items. + Nullable Maxresults; + + /// The timeout parameter is expressed in seconds. For more information, see Setting + /// Timeouts for Queue Service Operations. + Nullable Timeout; + + /// Specify to include additional, optional information. + Nullable> Include; + }; + + /** + * @brief CreateQueue operation options. + * + */ + struct QueuesClientCreateQueueOptions final + { + /// An opaque, globally-unique, client-generated string identifier for the request. + Nullable XMsClientRequestId; + + /// The timeout parameter is expressed in seconds. For more information, see Setting + /// Timeouts for Queue Service Operations. + Nullable Timeout; + + /// The metadata headers. + Nullable Metadata; + }; + + /** + * @brief GetPropertiesQueue operation options. + * + */ + struct QueuesClientGetPropertiesQueueOptions final + { + /// An opaque, globally-unique, client-generated string identifier for the request. + Nullable XMsClientRequestId; + + /// The timeout parameter is expressed in seconds. For more information, see Setting + /// Timeouts for Queue Service Operations. + Nullable Timeout; + }; + + /** + * @brief DeleteQueue operation options. + * + */ + struct QueuesClientDeleteQueueOptions final + { + /// An opaque, globally-unique, client-generated string identifier for the request. + Nullable XMsClientRequestId; + + /// The timeout parameter is expressed in seconds. For more information, see Setting + /// Timeouts for Queue Service Operations. + Nullable Timeout; + }; + + /** + * @brief SetMetadataQueue operation options. + * + */ + struct QueuesClientSetMetadataQueueOptions final + { + /// An opaque, globally-unique, client-generated string identifier for the request. + Nullable XMsClientRequestId; + + /// The timeout parameter is expressed in seconds. For more information, see Setting + /// Timeouts for Queue Service Operations. + Nullable Timeout; + + /// The metadata headers. + Nullable Metadata; + }; + + /** + * @brief GetAccessPolicyQueue operation options. + * + */ + struct QueuesClientGetAccessPolicyQueueOptions final + { + /// An opaque, globally-unique, client-generated string identifier for the request. + Nullable XMsClientRequestId; + + /// The timeout parameter is expressed in seconds. For more information, see Setting + /// Timeouts for Queue Service Operations. + Nullable Timeout; + }; + + /** + * @brief SetAccessPolicyQueue operation options. + * + */ + struct QueuesClientSetAccessPolicyQueueOptions final + { + /// An opaque, globally-unique, client-generated string identifier for the request. + Nullable XMsClientRequestId; + + /// The timeout parameter is expressed in seconds. For more information, see Setting + /// Timeouts for Queue Service Operations. + Nullable Timeout; + + /// The access control list. + Nullable QueueAcl; + }; + + /** + * @brief ReceiveMessagesQueue operation options. + * + */ + struct QueuesClientReceiveMessagesQueueOptions final + { + /// An opaque, globally-unique, client-generated string identifier for the request. + Nullable XMsClientRequestId; + + /// A nonzero integer value that specifies the number of messages to retrieve from the queue, up + /// to a maximum of 32. If fewer are visible, the visible messages are returned. By default, a + /// single message is retrieved from the queue with this operation. + Nullable Numofmessages; + + /// Specifies the new visibility timeout value, in seconds, relative to server time. A specified + /// value must be larger than or equal to 1 second, and cannot be larger than 7 days. The + /// visibility timeout of a message can be set to a value later than the expiry time. + Nullable Visibilitytimeout; + + /// The timeout parameter is expressed in seconds. For more information, see Setting + /// Timeouts for Queue Service Operations. + Nullable Timeout; + }; + + /** + * @brief ClearQueue operation options. + * + */ + struct QueuesClientClearQueueOptions final + { + /// An opaque, globally-unique, client-generated string identifier for the request. + Nullable XMsClientRequestId; + + /// The timeout parameter is expressed in seconds. For more information, see Setting + /// Timeouts for Queue Service Operations. + Nullable Timeout; + }; + + /** + * @brief SendMessageQueue operation options. + * + */ + struct QueuesClientSendMessageQueueOptions final + { + /// An opaque, globally-unique, client-generated string identifier for the request. + Nullable XMsClientRequestId; + + /// Specifies the new visibility timeout value, in seconds, relative to server time. A specified + /// value must be larger than or equal to 1 second, and cannot be larger than 7 days. The + /// visibility timeout of a message can be set to a value later than the expiry time. + Nullable Visibilitytimeout; + + /// Specifies the time-to-live interval for the message, in seconds. Prior to version + /// 2017-07-29, the maximum time-to-live allowed is 7 days. For version 2017-07-29 or later, the + /// maximum time-to-live can be any positive number, as well as -1 indicating that the message + /// does not expire. If this parameter is omitted, the default time-to-live is 7 days. + Nullable Messagettl; + + /// The timeout parameter is expressed in seconds. For more information, see Setting + /// Timeouts for Queue Service Operations. + Nullable Timeout; + }; + + /** + * @brief PeekMessagesQueue operation options. + * + */ + struct QueuesClientPeekMessagesQueueOptions final + { + /// An opaque, globally-unique, client-generated string identifier for the request. + Nullable XMsClientRequestId; + + /// A nonzero integer value that specifies the number of messages to retrieve from the queue, up + /// to a maximum of 32. If fewer are visible, the visible messages are returned. By default, a + /// single message is retrieved from the queue with this operation. + Nullable Numofmessages; + + /// The timeout parameter is expressed in seconds. For more information, see Setting + /// Timeouts for Queue Service Operations. + Nullable Timeout; + }; + + /** + * @brief UpdateMessageQueue operation options. + * + */ + struct QueuesClientUpdateMessageQueueOptions final + { + /// An opaque, globally-unique, client-generated string identifier for the request. + Nullable XMsClientRequestId; + + /// The timeout parameter is expressed in seconds. For more information, see Setting + /// Timeouts for Queue Service Operations. + Nullable Timeout; + + /// The queue message. The message must be in a format that can be included in an XML request + /// with UTF-8 encoding. The encoded message can be up to 64 KB in size. + Nullable QueueMessage; + }; + + /** + * @brief DeleteMessageQueue operation options. + * + */ + struct QueuesClientDeleteMessageQueueOptions final + { + /// An opaque, globally-unique, client-generated string identifier for the request. + Nullable XMsClientRequestId; + + /// The timeout parameter is expressed in seconds. For more information, see Setting + /// Timeouts for Queue Service Operations. + Nullable Timeout; + }; +}}}} // namespace Azure::Storage::Queues::_detail diff --git a/sdk/storage/azure-storage-queues/src/generated/queues_models.hpp b/sdk/storage/azure-storage-queues/src/generated/queues_models.hpp new file mode 100644 index 0000000000..fda7ed359b --- /dev/null +++ b/sdk/storage/azure-storage-queues/src/generated/queues_models.hpp @@ -0,0 +1,765 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// Code generated by Microsoft (R) TypeSpec Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +#pragma once + +#include "azure/storage/queues/dll_import_export.hpp" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace Azure { namespace Storage { namespace Queues { namespace _detail { namespace Models { + /** + * @brief The geo replication status. + * + */ + class GeoReplicationStatus final + : public Core::_internal::ExtendableEnumeration { + public: + /** + * @brief Constructs a new GeoReplicationStatus instance. + * + **/ + GeoReplicationStatus() = default; + + /** + * @brief Constructs a new GeoReplicationStatus instance from a string. + * @param geoReplicationStatus String value to construct the new instance from. + * + **/ + explicit GeoReplicationStatus(std::string geoReplicationStatus) + : ExtendableEnumeration(std::move(geoReplicationStatus)) + { + } + + /// The geo replication is live. + AZ_STORAGE_QUEUES_DLLEXPORT static const GeoReplicationStatus Live; + + /// The geo replication is bootstrap. + AZ_STORAGE_QUEUES_DLLEXPORT static const GeoReplicationStatus Bootstrap; + + /// The geo replication is unavailable. + AZ_STORAGE_QUEUES_DLLEXPORT static const GeoReplicationStatus Unavailable; + }; + + /** + * @brief Specify to include additional, optional information. + * + */ + class ListQueuesIncludeType final + : public Core::_internal::ExtendableEnumeration { + public: + /** + * @brief Constructs a new ListQueuesIncludeType instance. + * + **/ + ListQueuesIncludeType() = default; + + /** + * @brief Constructs a new ListQueuesIncludeType instance from a string. + * @param listQueuesIncludeType String value to construct the new instance from. + * + **/ + explicit ListQueuesIncludeType(std::string listQueuesIncludeType) + : ExtendableEnumeration(std::move(listQueuesIncludeType)) + { + } + + /// Include queue metadata. + AZ_STORAGE_QUEUES_DLLEXPORT static const ListQueuesIncludeType Metadata; + }; + + /** + * @brief SetPropertiesService operation result. + * + */ + struct SetPropertiesServiceResult final + { + /// Specifies the version of the operation to use for this request. + std::string Version; + + /// An opaque, globally-unique, server-generated string identifier for the request. + Nullable RequestId; + + /// An opaque, globally-unique, client-generated string identifier for the request. + Nullable ClientRequestId; + + /// UTC date/time value generated by the service that indicates the time at which the response + /// was initiated. + DateTime Date; + }; + + /** + * @brief The retention policy. + * + */ + struct RetentionPolicy final + { + /// Whether to enable the retention policy. + bool Enabled = false; + + /// The number of days to retain the logs. + Nullable Days; + }; + + /** + * @brief Azure Analytics Logging settings. + * + */ + struct Logging final + { + /// The version of the logging properties. + std::string Version; + + /// Whether delete operation is logged. + bool Delete = false; + + /// Whether read operation is logged. + bool Read = false; + + /// Whether write operation is logged. + bool Write = false; + + /// The retention policy of the logs. + Models::RetentionPolicy RetentionPolicy; + }; + + /** + * @brief The metrics properties. + * + */ + struct Metrics final + { + /// The version of the metrics properties. + Nullable Version; + + /// Whether it is enabled. + bool Enabled = false; + + /// Whether to include API in the metrics. + Nullable IncludeApis; + + /// The retention policy of the metrics. + Nullable RetentionPolicy; + }; + + /** + * @brief The CORS rules. + * + */ + struct CorsRule final + { + /// The allowed origins. + std::string AllowedOrigins; + + /// The allowed methods. + std::string AllowedMethods; + + /// The allowed headers. + std::string AllowedHeaders; + + /// The exposed headers. + std::string ExposedHeaders; + + /// The maximum age in seconds. + std::int32_t MaxAgeInSeconds = 0; + }; + + /** + * @brief The service properties. + * + */ + struct QueueServiceProperties final + { + /// The logging properties. + Nullable Logging; + + /// The hour metrics properties. + Nullable HourMetrics; + + /// The minute metrics properties. + Nullable MinuteMetrics; + + /// The CORS properties. + Nullable> Cors; + }; + + /** + * @brief The service properties. + * + */ + struct GetPropertiesServiceResult final + { + /// The logging properties. + Nullable Logging; + + /// The hour metrics properties. + Nullable HourMetrics; + + /// The minute metrics properties. + Nullable MinuteMetrics; + + /// The CORS properties. + Nullable> Cors; + + /// Specifies the version of the operation to use for this request. + std::string Version; + + /// An opaque, globally-unique, server-generated string identifier for the request. + Nullable RequestId; + + /// An opaque, globally-unique, client-generated string identifier for the request. + Nullable ClientRequestId; + + /// UTC date/time value generated by the service that indicates the time at which the response + /// was initiated. + DateTime Date; + }; + + /** + * @brief Geo replication information for the secondary storage location. + * + */ + struct GeoReplication final + { + /// The status of the secondary location. + GeoReplicationStatus Status; + + /// A GMT date/time value, to the second. All primary writes preceding this value are guaranteed + /// to be available for read operations at the secondary. Primary writes after this point in + /// time may or may not be available for reads. + Nullable LastSyncTime; + }; + + /** + * @brief Statistics returned to the C++ queue protocol client. + * + */ + struct GetStatisticsServiceResult final + { + /// The geo replication stats. + Nullable GeoReplication; + + /// Specifies the version of the operation to use for this request. + std::string Version; + + /// An opaque, globally-unique, server-generated string identifier for the request. + Nullable RequestId; + + /// An opaque, globally-unique, client-generated string identifier for the request. + Nullable ClientRequestId; + + /// UTC date/time value generated by the service that indicates the time at which the response + /// was initiated. + DateTime Date; + }; + + /** + * @brief A user delegation key. + * + */ + struct GetUserDelegationKeyServiceResult final + { + /// The Entra ID object ID in GUID format. + std::string SignedOid; + + /// The Entra ID tenant ID in GUID format. + std::string SignedTid; + + /// The date-time the key is active. + DateTime SignedStart; + + /// The date-time the key expires. + DateTime SignedExpiry; + + /// The service that created the key. + std::string SignedService; + + /// The service version used when creating the key. + std::string SignedVersion; + + /// The delegated user tenant ID in Entra ID. Return if DelegatedUserTid is specified. + Nullable SignedDelegatedUserTid; + + /// The key as a base64 string. + std::string Value; + + /// Specifies the version of the operation to use for this request. + std::string Version; + + /// An opaque, globally-unique, server-generated string identifier for the request. + Nullable RequestId; + + /// An opaque, globally-unique, client-generated string identifier for the request. + Nullable ClientRequestId; + + /// UTC date/time value generated by the service that indicates the time at which the response + /// was initiated. + DateTime Date; + }; + + /** + * @brief Key information for user delegation key. + * + */ + struct KeyInfo final + { + /// The date-time the key is active in ISO 8601 UTC time. + Nullable Start; + + /// The date-time the key expires in ISO 8601 UTC time. + std::string Expiry; + + /// The delegated user tenant ID in Entra ID. + Nullable DelegatedUserTid; + }; + + /** + * @brief An Azure Storage Queue. + * + */ + struct QueueItem final + { + /// The name of the queue. + std::string Name; + + /// The metadata of the queue. + Nullable> Metadata; + }; + + /** + * @brief The list queues response. + * + */ + struct GetQueuesServiceResult final + { + /// The service endpoint. + std::string ServiceEndpoint; + + /// The prefix of the queues. + std::string Prefix; + + /// Identifies the current position in the list queues operation. + Nullable Marker; + + /// The max results. + std::int32_t MaxResults = 0; + + /// The list of queues. + Nullable> QueueItems; + + /// Identifies the portion of the list of queues to be returned with the next listing operation. + std::string NextMarker; + + /// Specifies the version of the operation to use for this request. + std::string Version; + + /// An opaque, globally-unique, server-generated string identifier for the request. + Nullable RequestId; + + /// An opaque, globally-unique, client-generated string identifier for the request. + Nullable ClientRequestId; + + /// UTC date/time value generated by the service that indicates the time at which the response + /// was initiated. + DateTime Date; + }; + + /** + * @brief CreateQueue operation result. + * + */ + struct CreateQueueResult final + { + /// Specifies the version of the operation to use for this request. + std::string Version; + + /// An opaque, globally-unique, server-generated string identifier for the request. + Nullable RequestId; + + /// An opaque, globally-unique, client-generated string identifier for the request. + Nullable ClientRequestId; + + /// UTC date/time value generated by the service that indicates the time at which the response + /// was initiated. + DateTime Date; + }; + + /** + * @brief GetPropertiesQueue operation result. + * + */ + struct GetPropertiesQueueResult final + { + /// The metadata headers. + Nullable 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. + Nullable ApproximateMessagesCount; + + /// Specifies the version of the operation to use for this request. + std::string Version; + + /// An opaque, globally-unique, server-generated string identifier for the request. + Nullable RequestId; + + /// An opaque, globally-unique, client-generated string identifier for the request. + Nullable ClientRequestId; + + /// UTC date/time value generated by the service that indicates the time at which the response + /// was initiated. + DateTime Date; + }; + + /** + * @brief DeleteQueue operation result. + * + */ + struct DeleteQueueResult final + { + /// Specifies the version of the operation to use for this request. + std::string Version; + + /// An opaque, globally-unique, server-generated string identifier for the request. + Nullable RequestId; + + /// An opaque, globally-unique, client-generated string identifier for the request. + Nullable ClientRequestId; + + /// UTC date/time value generated by the service that indicates the time at which the response + /// was initiated. + DateTime Date; + }; + + /** + * @brief SetMetadataQueue operation result. + * + */ + struct SetMetadataQueueResult final + { + /// Specifies the version of the operation to use for this request. + std::string Version; + + /// An opaque, globally-unique, server-generated string identifier for the request. + Nullable RequestId; + + /// An opaque, globally-unique, client-generated string identifier for the request. + Nullable ClientRequestId; + + /// UTC date/time value generated by the service that indicates the time at which the response + /// was initiated. + DateTime Date; + }; + + /** + * @brief The access policy. + * + */ + struct AccessPolicy final + { + /// The date-time the policy is active. + Nullable Start; + + /// The date-time the policy expires. + Nullable Expiry; + + /// The permissions for the policy. + Nullable Permission; + }; + + /** + * @brief The signed identifier. + * + */ + struct SignedIdentifier final + { + /// The unique ID for the signed identifier. + std::string Id; + + /// The access policy for the signed identifier. + Models::AccessPolicy AccessPolicy; + }; + + /** + * @brief An array of signed identifiers. + * + */ + struct GetAccessPolicyQueueResult final + { + /// The list of signed identifiers. + std::vector Items; + + /// Specifies the version of the operation to use for this request. + std::string Version; + + /// An opaque, globally-unique, server-generated string identifier for the request. + Nullable RequestId; + + /// An opaque, globally-unique, client-generated string identifier for the request. + Nullable ClientRequestId; + + /// UTC date/time value generated by the service that indicates the time at which the response + /// was initiated. + DateTime Date; + }; + + /** + * @brief SetAccessPolicyQueue operation result. + * + */ + struct SetAccessPolicyQueueResult final + { + /// Specifies the version of the operation to use for this request. + std::string Version; + + /// An opaque, globally-unique, server-generated string identifier for the request. + Nullable RequestId; + + /// An opaque, globally-unique, client-generated string identifier for the request. + Nullable ClientRequestId; + + /// UTC date/time value generated by the service that indicates the time at which the response + /// was initiated. + DateTime Date; + }; + + /** + * @brief An array of signed identifiers. + * + */ + struct SignedIdentifiers final + { + /// The list of signed identifiers. + std::vector Items; + }; + + /** + * @brief The received queue message. + * + */ + struct ReceivedMessage final + { + /// The ID of the message. + std::string MessageId; + + /// The time the message was inserted into the queue. + DateTime InsertionTime; + + /// The time that the message will expire and be automatically deleted. + DateTime ExpirationTime; + + /// An opaque value required to delete the message. If deletion fails using this PopReceipt then + /// the message has been dequeued by another client. + std::string PopReceipt; + + /// The time that the message will again become visible in the queue. + DateTime TimeNextVisible; + + /// The number of times the message has been dequeued. + std::int64_t DequeueCount = 0; + + /// The content of the message. + std::string MessageText; + }; + + /** + * @brief The response of receive messages. + * + */ + struct ReceiveMessagesQueueResult final + { + /// The list of received messages. + std::vector Items; + + /// Specifies the version of the operation to use for this request. + std::string Version; + + /// An opaque, globally-unique, server-generated string identifier for the request. + Nullable RequestId; + + /// An opaque, globally-unique, client-generated string identifier for the request. + Nullable ClientRequestId; + + /// UTC date/time value generated by the service that indicates the time at which the response + /// was initiated. + DateTime Date; + }; + + /** + * @brief ClearQueue operation result. + * + */ + struct ClearQueueResult final + { + /// Specifies the version of the operation to use for this request. + std::string Version; + + /// An opaque, globally-unique, server-generated string identifier for the request. + Nullable RequestId; + + /// An opaque, globally-unique, client-generated string identifier for the request. + Nullable ClientRequestId; + + /// UTC date/time value generated by the service that indicates the time at which the response + /// was initiated. + DateTime Date; + }; + + /** + * @brief The sent queue message. + * + */ + struct SentMessage final + { + /// The ID of the message. + std::string MessageId; + + /// The time the message was inserted into the queue. + DateTime InsertionTime; + + /// The time that the message will expire and be automatically deleted. + DateTime ExpirationTime; + + /// An opaque value required to delete the message. If deletion fails using this PopReceipt then + /// the message has been dequeued by another client. + std::string PopReceipt; + + /// The time that the message will again become visible in the queue. + DateTime TimeNextVisible; + }; + + /** + * @brief The response of send message. + * + */ + struct SendMessageQueueResult final + { + /// The list of sent messages. + std::vector Items; + + /// Specifies the version of the operation to use for this request. + std::string Version; + + /// An opaque, globally-unique, server-generated string identifier for the request. + Nullable RequestId; + + /// An opaque, globally-unique, client-generated string identifier for the request. + Nullable ClientRequestId; + + /// UTC date/time value generated by the service that indicates the time at which the response + /// was initiated. + DateTime Date; + }; + + /** + * @brief The queue message. + * + */ + struct QueueMessage final + { + /// The content of the message. + std::string MessageText; + }; + + /** + * @brief The peeked queue message. + * + */ + struct PeekedMessage final + { + /// The ID of the message. + std::string MessageId; + + /// The time the message was inserted into the queue. + DateTime InsertionTime; + + /// The time that the message will expire and be automatically deleted. + DateTime ExpirationTime; + + /// The number of times the message has been dequeued. + std::int64_t DequeueCount = 0; + + /// The content of the message. + std::string MessageText; + }; + + /** + * @brief The response of peek messages. + * + */ + struct PeekMessagesQueueResult final + { + /// The list of peeked messages. + std::vector Items; + + /// Specifies the version of the operation to use for this request. + std::string Version; + + /// An opaque, globally-unique, server-generated string identifier for the request. + Nullable RequestId; + + /// An opaque, globally-unique, client-generated string identifier for the request. + Nullable ClientRequestId; + + /// UTC date/time value generated by the service that indicates the time at which the response + /// was initiated. + DateTime Date; + }; + + /** + * @brief UpdateMessageQueue operation result. + * + */ + struct UpdateMessageQueueResult final + { + /// The pop receipt of the queue message. + std::string PopReceipt; + + /// A UTC date/time value that represents when the message will be visible on the queue. + DateTime TimeNextVisible; + + /// Specifies the version of the operation to use for this request. + std::string Version; + + /// An opaque, globally-unique, server-generated string identifier for the request. + Nullable RequestId; + + /// An opaque, globally-unique, client-generated string identifier for the request. + Nullable ClientRequestId; + + /// UTC date/time value generated by the service that indicates the time at which the response + /// was initiated. + DateTime Date; + }; + + /** + * @brief DeleteMessageQueue operation result. + * + */ + struct DeleteMessageQueueResult final + { + /// Specifies the version of the operation to use for this request. + std::string Version; + + /// An opaque, globally-unique, server-generated string identifier for the request. + Nullable RequestId; + + /// An opaque, globally-unique, client-generated string identifier for the request. + Nullable ClientRequestId; + + /// UTC date/time value generated by the service that indicates the time at which the response + /// was initiated. + DateTime Date; + }; +}}}}} // namespace Azure::Storage::Queues::_detail::Models diff --git a/sdk/storage/azure-storage-queues/src/private/queue_model_conversions.hpp b/sdk/storage/azure-storage-queues/src/private/queue_model_conversions.hpp new file mode 100644 index 0000000000..6c6f25344f --- /dev/null +++ b/sdk/storage/azure-storage-queues/src/private/queue_model_conversions.hpp @@ -0,0 +1,189 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#pragma once + +#include "../generated/queues_client.hpp" +#include "azure/storage/queues/queue_responses.hpp" + +namespace Azure { namespace Storage { namespace Queues { namespace _detail { + + namespace PublicModels = Azure::Storage::Queues::Models; + + inline Models::RetentionPolicy ToGenerated(PublicModels::RetentionPolicy value) + { + Models::RetentionPolicy result; + result.Enabled = value.IsEnabled; + result.Days = value.Days; + return result; + } + + inline PublicModels::RetentionPolicy ToPublic(Models::RetentionPolicy value) + { + PublicModels::RetentionPolicy result; + result.IsEnabled = value.Enabled; + result.Days = value.Days; + return result; + } + + inline Models::Metrics ToGenerated(PublicModels::Metrics value) + { + Models::Metrics result; + result.Version = std::move(value.Version); + result.Enabled = value.IsEnabled; + result.IncludeApis = value.IncludeApis; + result.RetentionPolicy = ToGenerated(std::move(value.RetentionPolicy)); + return result; + } + + inline PublicModels::Metrics ToPublic(Models::Metrics value) + { + PublicModels::Metrics result; + if (value.Version.HasValue()) + { + result.Version = std::move(value.Version.Value()); + } + result.IsEnabled = value.Enabled; + result.IncludeApis = value.IncludeApis; + if (value.RetentionPolicy.HasValue()) + { + result.RetentionPolicy = ToPublic(std::move(value.RetentionPolicy.Value())); + } + return result; + } + + inline Models::CorsRule ToGenerated(PublicModels::CorsRule value) + { + Models::CorsRule result; + result.AllowedOrigins = std::move(value.AllowedOrigins); + result.AllowedMethods = std::move(value.AllowedMethods); + result.AllowedHeaders = std::move(value.AllowedHeaders); + result.ExposedHeaders = std::move(value.ExposedHeaders); + result.MaxAgeInSeconds = value.MaxAgeInSeconds; + return result; + } + + inline PublicModels::CorsRule ToPublic(Models::CorsRule value) + { + PublicModels::CorsRule result; + result.AllowedOrigins = std::move(value.AllowedOrigins); + result.AllowedMethods = std::move(value.AllowedMethods); + result.AllowedHeaders = std::move(value.AllowedHeaders); + result.ExposedHeaders = std::move(value.ExposedHeaders); + result.MaxAgeInSeconds = value.MaxAgeInSeconds; + return result; + } + + inline Models::QueueServiceProperties ToGenerated(PublicModels::QueueServiceProperties value) + { + Models::QueueServiceProperties result; + Models::Logging logging; + logging.Version = std::move(value.Logging.Version); + logging.Delete = value.Logging.Delete; + logging.Read = value.Logging.Read; + logging.Write = value.Logging.Write; + logging.RetentionPolicy = ToGenerated(std::move(value.Logging.RetentionPolicy)); + result.Logging = std::move(logging); + result.HourMetrics = ToGenerated(std::move(value.HourMetrics)); + result.MinuteMetrics = ToGenerated(std::move(value.MinuteMetrics)); + std::vector cors; + cors.reserve(value.Cors.size()); + for (auto& rule : value.Cors) + { + cors.emplace_back(ToGenerated(std::move(rule))); + } + result.Cors = std::move(cors); + return result; + } + + inline PublicModels::QueueServiceProperties ToPublic(Models::GetPropertiesServiceResult value) + { + PublicModels::QueueServiceProperties result; + if (value.Logging.HasValue()) + { + auto& logging = value.Logging.Value(); + result.Logging.Version = std::move(logging.Version); + result.Logging.Delete = logging.Delete; + result.Logging.Read = logging.Read; + result.Logging.Write = logging.Write; + result.Logging.RetentionPolicy = ToPublic(std::move(logging.RetentionPolicy)); + } + if (value.HourMetrics.HasValue()) + { + result.HourMetrics = ToPublic(std::move(value.HourMetrics.Value())); + } + if (value.MinuteMetrics.HasValue()) + { + result.MinuteMetrics = ToPublic(std::move(value.MinuteMetrics.Value())); + } + if (value.Cors.HasValue()) + { + result.Cors.reserve(value.Cors.Value().size()); + for (auto& rule : value.Cors.Value()) + { + result.Cors.emplace_back(ToPublic(std::move(rule))); + } + } + return result; + } + + inline Models::SignedIdentifiers ToGenerated(PublicModels::QueueAccessPolicy const& value) + { + Models::SignedIdentifiers result; + result.Items.reserve(value.SignedIdentifiers.size()); + for (auto const& identifier : value.SignedIdentifiers) + { + Models::SignedIdentifier item; + item.Id = identifier.Id; + item.AccessPolicy.Start = identifier.StartsOn; + item.AccessPolicy.Expiry = identifier.ExpiresOn; + item.AccessPolicy.Permission = identifier.Permissions; + result.Items.emplace_back(std::move(item)); + } + return result; + } + + inline PublicModels::QueueAccessPolicy ToPublic(Models::GetAccessPolicyQueueResult value) + { + PublicModels::QueueAccessPolicy result; + result.SignedIdentifiers.reserve(value.Items.size()); + for (auto& identifier : value.Items) + { + PublicModels::SignedIdentifier item; + item.Id = std::move(identifier.Id); + item.StartsOn = identifier.AccessPolicy.Start; + item.ExpiresOn = identifier.AccessPolicy.Expiry; + if (identifier.AccessPolicy.Permission.HasValue()) + { + item.Permissions = std::move(identifier.AccessPolicy.Permission.Value()); + } + result.SignedIdentifiers.emplace_back(std::move(item)); + } + return result; + } + + inline PublicModels::QueueMessage ToPublic(Models::ReceivedMessage value) + { + PublicModels::QueueMessage result; + result.MessageId = std::move(value.MessageId); + result.InsertedOn = value.InsertionTime; + result.ExpiresOn = value.ExpirationTime; + result.PopReceipt = std::move(value.PopReceipt); + result.NextVisibleOn = value.TimeNextVisible; + result.DequeueCount = value.DequeueCount; + result.MessageText = std::move(value.MessageText); + return result; + } + + inline PublicModels::PeekedQueueMessage ToPublic(Models::PeekedMessage value) + { + PublicModels::PeekedQueueMessage result; + result.MessageId = std::move(value.MessageId); + result.InsertedOn = value.InsertionTime; + result.ExpiresOn = value.ExpirationTime; + result.DequeueCount = value.DequeueCount; + result.MessageText = std::move(value.MessageText); + return result; + } + +}}}} // namespace Azure::Storage::Queues::_detail diff --git a/sdk/storage/azure-storage-queues/src/private/queue_protocol.hpp b/sdk/storage/azure-storage-queues/src/private/queue_protocol.hpp new file mode 100644 index 0000000000..15125418c9 --- /dev/null +++ b/sdk/storage/azure-storage-queues/src/private/queue_protocol.hpp @@ -0,0 +1,76 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#pragma once + +#include +#include +#include +#include + +#include + +namespace Azure { namespace Storage { namespace Queues { namespace _detail { + + class QueueProtocolPolicy final : public Core::Http::Policies::HttpPolicy { + public: + std::unique_ptr Clone() const override + { + return std::make_unique(); + } + + std::unique_ptr Send( + Core::Http::Request& request, + Core::Http::Policies::NextHttpPolicy nextPolicy, + Core::Context const& context) const override + { + // The previous protocol layer did not set Accept. User policies can still set it. + request.RemoveHeader("Accept"); + auto const contentType = request.GetHeader("Content-Type"); + if (contentType.HasValue() && contentType.Value() == "application/xml") + { + if (request.GetBodyStream()->Length() == 0) + { + request.RemoveHeader("Content-Type"); + } + else + { + request.SetHeader("Content-Type", "application/xml; charset=UTF-8"); + } + } + return nextPolicy.Send(request, context); + } + }; + + inline std::vector> BuildQueuePipelinePolicies( + Core::_internal::ClientOptions const& options, + _internal::BuildStoragePipelineOptions pipelineOptions) + { + auto policies = _internal::BuildHttpPipelinePolicies(options, std::move(pipelineOptions)); + policies.insert(policies.begin(), std::make_unique()); + return policies; + } + + template + auto InvokeStorageProtocol(Operation&& operation) + -> decltype(std::forward(operation)()) + { + try + { + return std::forward(operation)(); + } + catch (StorageException const&) + { + throw; + } + catch (Core::RequestFailedException& error) + { + if (!error.RawResponse) + { + throw; + } + throw StorageException::CreateFromResponse(std::move(error.RawResponse)); + } + } + +}}}} // namespace Azure::Storage::Queues::_detail diff --git a/sdk/storage/azure-storage-queues/src/queue_client.cpp b/sdk/storage/azure-storage-queues/src/queue_client.cpp index 1fdc9dd97f..930282ba7d 100644 --- a/sdk/storage/azure-storage-queues/src/queue_client.cpp +++ b/sdk/storage/azure-storage-queues/src/queue_client.cpp @@ -4,6 +4,8 @@ #include "azure/storage/queues/queue_client.hpp" #include "private/package_version.hpp" +#include "private/queue_model_conversions.hpp" +#include "private/queue_protocol.hpp" #include #include @@ -13,6 +15,8 @@ #include #include +#include + namespace Azure { namespace Storage { namespace Queues { QueueClient QueueClient::CreateFromConnectionString( @@ -49,7 +53,7 @@ namespace Azure { namespace Storage { namespace Queues { pipelineOptions.SharedKeyAuthPolicy = std::make_unique<_internal::SharedKeyPolicy>(credential); m_pipeline = std::make_shared( - _internal::BuildHttpPipelinePolicies(options, std::move(pipelineOptions))); + _detail::BuildQueuePipelinePolicies(options, std::move(pipelineOptions))); } QueueClient::QueueClient( @@ -76,7 +80,7 @@ namespace Azure { namespace Storage { namespace Queues { } m_pipeline = std::make_shared( - _internal::BuildHttpPipelinePolicies(options, std::move(pipelineOptions))); + _detail::BuildQueuePipelinePolicies(options, std::move(pipelineOptions))); } QueueClient::QueueClient(const std::string& queueUrl, const QueueClientOptions& options) @@ -90,7 +94,7 @@ namespace Azure { namespace Storage { namespace Queues { pipelineOptions.ApiVersion = options.ApiVersion.ToString(); m_pipeline = std::make_shared( - _internal::BuildHttpPipelinePolicies(options, std::move(pipelineOptions))); + _detail::BuildQueuePipelinePolicies(options, std::move(pipelineOptions))); } Azure::Response QueueClient::Create( @@ -99,14 +103,16 @@ namespace Azure { namespace Storage { namespace Queues { { try { - _detail::QueueClient::CreateQueueOptions protocolLayerOptions; - protocolLayerOptions.Metadata - = std::map(options.Metadata.begin(), options.Metadata.end()); - auto response - = _detail::QueueClient::Create(*m_pipeline, m_queueUrl, protocolLayerOptions, context); - response.Value.Created - = response.RawResponse->GetStatusCode() == Core::Http::HttpStatusCode::Created; - return response; + _detail::QueuesClientCreateQueueOptions protocolLayerOptions; + protocolLayerOptions.Metadata = options.Metadata; + auto response = _detail::InvokeStorageProtocol([&] { + return _detail::QueuesClient(m_queueUrl, m_pipeline) + .CreateQueue(protocolLayerOptions, context); + }); + Models::CreateQueueResult result; + result.Created = response.RawResponse->GetStatusCode() == Core::Http::HttpStatusCode::Created; + return Azure::Response( + std::move(result), std::move(response.RawResponse)); } catch (StorageException& e) { @@ -127,8 +133,9 @@ namespace Azure { namespace Storage { namespace Queues { (void)options; try { - _detail::QueueClient::DeleteQueueOptions protocolLayerOptions; - return _detail::QueueClient::Delete(*m_pipeline, m_queueUrl, protocolLayerOptions, context); + auto response = _detail::InvokeStorageProtocol( + [&] { return _detail::QueuesClient(m_queueUrl, m_pipeline).DeleteQueue({}, context); }); + return Azure::Response({}, std::move(response.RawResponse)); } catch (StorageException& e) { @@ -147,21 +154,28 @@ namespace Azure { namespace Storage { namespace Queues { const Azure::Core::Context& context) const { (void)options; - _detail::QueueClient::GetQueuePropertiesOptions protocolLayerOptions; - auto ret = _detail::QueueClient::GetProperties( - *m_pipeline, m_queueUrl, protocolLayerOptions, context); + auto ret = _detail::InvokeStorageProtocol([&] { + return _detail::QueuesClient(m_queueUrl, m_pipeline).GetPropertiesQueue({}, context); + }); Models::QueueProperties queueProperties; - queueProperties.Metadata = std::move(ret.Value.Metadata); - if (ret.Value.ApproximateMessageCount > static_cast(INT32_MAX)) + if (ret.Value.Metadata.HasValue()) + { + queueProperties.Metadata = std::move(ret.Value.Metadata.Value()); + } + if (!ret.Value.ApproximateMessagesCount.HasValue()) + { + throw std::out_of_range("Missing x-ms-approximate-messages-count response header."); + } + auto const count = ret.Value.ApproximateMessagesCount.Value(); + if (count > static_cast(INT32_MAX)) { queueProperties.ApproximateMessageCount = -1; } else { - queueProperties.ApproximateMessageCount - = static_cast(ret.Value.ApproximateMessageCount); + queueProperties.ApproximateMessageCount = static_cast(count); } - queueProperties.ApproximateMessageCountLong = ret.Value.ApproximateMessageCount; + queueProperties.ApproximateMessageCountLong = count; return Azure::Response( std::move(queueProperties), std::move(ret.RawResponse)); } @@ -172,11 +186,13 @@ namespace Azure { namespace Storage { namespace Queues { const Azure::Core::Context& context) const { (void)options; - _detail::QueueClient::SetQueueMetadataOptions protocolLayerOptions; - protocolLayerOptions.Metadata - = std::map(metadata.begin(), metadata.end()); - return _detail::QueueClient::SetMetadata( - *m_pipeline, m_queueUrl, protocolLayerOptions, context); + _detail::QueuesClientSetMetadataQueueOptions protocolLayerOptions; + protocolLayerOptions.Metadata = std::move(metadata); + auto response = _detail::InvokeStorageProtocol([&] { + return _detail::QueuesClient(m_queueUrl, m_pipeline) + .SetMetadataQueue(protocolLayerOptions, context); + }); + return Azure::Response({}, std::move(response.RawResponse)); } Azure::Response QueueClient::GetAccessPolicy( @@ -184,9 +200,11 @@ namespace Azure { namespace Storage { namespace Queues { const Azure::Core::Context& context) const { (void)options; - _detail::QueueClient::GetQueueAccessPolicyOptions protocolLayerOptions; - return _detail::QueueClient::GetAccessPolicy( - *m_pipeline, m_queueUrl, protocolLayerOptions, context); + auto response = _detail::InvokeStorageProtocol([&] { + return _detail::QueuesClient(m_queueUrl, m_pipeline).GetAccessPolicyQueue({}, context); + }); + return Azure::Response( + _detail::ToPublic(std::move(response.Value)), std::move(response.RawResponse)); } Azure::Response QueueClient::SetAccessPolicy( @@ -195,10 +213,13 @@ namespace Azure { namespace Storage { namespace Queues { const Azure::Core::Context& context) const { (void)options; - _detail::QueueClient::SetQueueAccessPolicyOptions protocolLayerOptions; - protocolLayerOptions.QueueAcl = accessPolicy.SignedIdentifiers; - return _detail::QueueClient::SetAccessPolicy( - *m_pipeline, m_queueUrl, protocolLayerOptions, context); + _detail::QueuesClientSetAccessPolicyQueueOptions protocolLayerOptions; + protocolLayerOptions.QueueAcl = _detail::ToGenerated(accessPolicy); + auto response = _detail::InvokeStorageProtocol([&] { + return _detail::QueuesClient(m_queueUrl, m_pipeline) + .SetAccessPolicyQueue(protocolLayerOptions, context); + }); + return Azure::Response({}, std::move(response.RawResponse)); } Azure::Response QueueClient::EnqueueMessage( @@ -206,52 +227,79 @@ namespace Azure { namespace Storage { namespace Queues { const EnqueueMessageOptions& options, const Azure::Core::Context& context) const { - auto messagesUrl = m_queueUrl; - messagesUrl.AppendPath("messages"); - _detail::QueueClient::EnqueueQueueMessageOptions protocolLayerOptions; - protocolLayerOptions.QueueMessage.MessageText = std::move(messageText); + _detail::QueuesClientSendMessageQueueOptions protocolLayerOptions; + _detail::Models::QueueMessage message; + message.MessageText = std::move(messageText); if (options.TimeToLive.HasValue()) { - protocolLayerOptions.MessageTimeToLive - = static_cast(options.TimeToLive.Value().count()); + protocolLayerOptions.Messagettl = static_cast(options.TimeToLive.Value().count()); } if (options.VisibilityTimeout.HasValue()) { protocolLayerOptions.Visibilitytimeout = static_cast(options.VisibilityTimeout.Value().count()); } - return _detail::QueueClient::EnqueueMessage( - *m_pipeline, messagesUrl, protocolLayerOptions, context); + auto response = _detail::InvokeStorageProtocol([&] { + return _detail::QueuesClient(m_queueUrl, m_pipeline) + .SendMessageQueue(message, protocolLayerOptions, context); + }); + Models::EnqueueMessageResult result; + if (!response.Value.Items.empty()) + { + auto& item = response.Value.Items.front(); + result.MessageId = std::move(item.MessageId); + result.InsertedOn = item.InsertionTime; + result.ExpiresOn = item.ExpirationTime; + result.PopReceipt = std::move(item.PopReceipt); + result.NextVisibleOn = item.TimeNextVisible; + } + return Azure::Response( + std::move(result), std::move(response.RawResponse)); } Azure::Response QueueClient::ReceiveMessages( const ReceiveMessagesOptions& options, const Azure::Core::Context& context) const { - auto messagesUrl = m_queueUrl; - messagesUrl.AppendPath("messages"); - _detail::QueueClient::ReceiveQueueMessagesOptions protocolLayerOptions; - protocolLayerOptions.NumberOfMessages = options.MaxMessages; + _detail::QueuesClientReceiveMessagesQueueOptions protocolLayerOptions; + protocolLayerOptions.Numofmessages = options.MaxMessages; if (options.VisibilityTimeout.HasValue()) { protocolLayerOptions.Visibilitytimeout = static_cast(options.VisibilityTimeout.Value().count()); } - return _detail::QueueClient::ReceiveMessages( - *m_pipeline, messagesUrl, protocolLayerOptions, context); + auto response = _detail::InvokeStorageProtocol([&] { + return _detail::QueuesClient(m_queueUrl, m_pipeline) + .ReceiveMessagesQueue(protocolLayerOptions, context); + }); + Models::ReceivedMessages result; + result.Messages.reserve(response.Value.Items.size()); + for (auto& item : response.Value.Items) + { + result.Messages.emplace_back(_detail::ToPublic(std::move(item))); + } + return Azure::Response( + std::move(result), std::move(response.RawResponse)); } Azure::Response QueueClient::PeekMessages( const PeekMessagesOptions& options, const Azure::Core::Context& context) const { - (void)options; - auto messagesUrl = m_queueUrl; - messagesUrl.AppendPath("messages"); - _detail::QueueClient::PeekQueueMessagesOptions protocolLayerOptions; - protocolLayerOptions.NumberOfMessages = options.MaxMessages; - return _detail::QueueClient::PeekMessages( - *m_pipeline, messagesUrl, protocolLayerOptions, context); + _detail::QueuesClientPeekMessagesQueueOptions protocolLayerOptions; + protocolLayerOptions.Numofmessages = options.MaxMessages; + auto response = _detail::InvokeStorageProtocol([&] { + return _detail::QueuesClient(m_queueUrl, m_pipeline) + .PeekMessagesQueue(protocolLayerOptions, context); + }); + Models::PeekedMessages result; + result.Messages.reserve(response.Value.Items.size()); + for (auto& item : response.Value.Items) + { + result.Messages.emplace_back(_detail::ToPublic(std::move(item))); + } + return Azure::Response( + std::move(result), std::move(response.RawResponse)); } Azure::Response QueueClient::UpdateMessage( @@ -261,26 +309,27 @@ namespace Azure { namespace Storage { namespace Queues { const UpdateMessageOptions& options, const Azure::Core::Context& context) const { - auto messageUrl = m_queueUrl; - messageUrl.AppendPath("messages"); - messageUrl.AppendPath(_internal::UrlEncodePath(messageId)); + _detail::QueuesClientUpdateMessageQueueOptions protocolLayerOptions; if (options.MessageText.HasValue()) { - _detail::QueueClient::UpdateQueueMessageOptions protocolLayerOptions; - protocolLayerOptions.QueueMessage.MessageText = options.MessageText.Value(); - protocolLayerOptions.PopReceipt = popReceipt; - protocolLayerOptions.Visibilitytimeout = static_cast(visibilityTimeout.count()); - return _detail::QueueClient::UpdateMessage( - *m_pipeline, messageUrl, protocolLayerOptions, context); - } - else - { - _detail::QueueClient::UpdateQueueMessageVisibilityOptions protocolLayerOptions; - protocolLayerOptions.PopReceipt = popReceipt; - protocolLayerOptions.Visibilitytimeout = static_cast(visibilityTimeout.count()); - return _detail::QueueClient::UpdateMessageVisibility( - *m_pipeline, messageUrl, protocolLayerOptions, context); + _detail::Models::QueueMessage message; + message.MessageText = options.MessageText.Value(); + protocolLayerOptions.QueueMessage = std::move(message); } + auto response = _detail::InvokeStorageProtocol([&] { + return _detail::QueuesClient(m_queueUrl, m_pipeline) + .UpdateMessageQueue( + messageId, + popReceipt, + static_cast(visibilityTimeout.count()), + protocolLayerOptions, + context); + }); + Models::UpdateMessageResult result; + result.PopReceipt = std::move(response.Value.PopReceipt); + result.NextVisibleOn = response.Value.TimeNextVisible; + return Azure::Response( + std::move(result), std::move(response.RawResponse)); } Azure::Response QueueClient::DeleteMessage( @@ -290,13 +339,11 @@ namespace Azure { namespace Storage { namespace Queues { const Azure::Core::Context& context) const { (void)options; - auto messageUrl = m_queueUrl; - messageUrl.AppendPath("messages"); - messageUrl.AppendPath(_internal::UrlEncodePath(messageId)); - _detail::QueueClient::DeleteQueueMessageOptions protocolLayerOptions; - protocolLayerOptions.PopReceipt = popReceipt; - return _detail::QueueClient::DeleteMessage( - *m_pipeline, messageUrl, protocolLayerOptions, context); + auto response = _detail::InvokeStorageProtocol([&] { + return _detail::QueuesClient(m_queueUrl, m_pipeline) + .DeleteMessageQueue(messageId, popReceipt, {}, context); + }); + return Azure::Response({}, std::move(response.RawResponse)); } Azure::Response QueueClient::ClearMessages( @@ -304,11 +351,9 @@ namespace Azure { namespace Storage { namespace Queues { const Azure::Core::Context& context) const { (void)options; - auto messagesUrl = m_queueUrl; - messagesUrl.AppendPath("messages"); - _detail::QueueClient::ClearQueueMessagesOptions protocolLayerOptions; - return _detail::QueueClient::ClearMessages( - *m_pipeline, messagesUrl, protocolLayerOptions, context); + auto response = _detail::InvokeStorageProtocol( + [&] { return _detail::QueuesClient(m_queueUrl, m_pipeline).ClearQueue({}, context); }); + return Azure::Response({}, std::move(response.RawResponse)); } }}} // namespace Azure::Storage::Queues diff --git a/sdk/storage/azure-storage-queues/src/queue_responses.cpp b/sdk/storage/azure-storage-queues/src/queue_responses.cpp index 8ed00b183d..50e4b9970b 100644 --- a/sdk/storage/azure-storage-queues/src/queue_responses.cpp +++ b/sdk/storage/azure-storage-queues/src/queue_responses.cpp @@ -7,6 +7,12 @@ namespace Azure { namespace Storage { namespace Queues { + namespace Models { + const GeoReplicationStatus GeoReplicationStatus::Live("live"); + const GeoReplicationStatus GeoReplicationStatus::Bootstrap("bootstrap"); + const GeoReplicationStatus GeoReplicationStatus::Unavailable("unavailable"); + } // namespace Models + void ListQueuesPagedResponse::OnNextPage(const Azure::Core::Context& context) { m_operationOptions.ContinuationToken = NextPageToken; diff --git a/sdk/storage/azure-storage-queues/src/queue_service_client.cpp b/sdk/storage/azure-storage-queues/src/queue_service_client.cpp index bb019efbd6..17ecfe18dc 100644 --- a/sdk/storage/azure-storage-queues/src/queue_service_client.cpp +++ b/sdk/storage/azure-storage-queues/src/queue_service_client.cpp @@ -4,6 +4,8 @@ #include "azure/storage/queues/queue_service_client.hpp" #include "private/package_version.hpp" +#include "private/queue_model_conversions.hpp" +#include "private/queue_protocol.hpp" #include #include @@ -48,7 +50,7 @@ namespace Azure { namespace Storage { namespace Queues { pipelineOptions.SharedKeyAuthPolicy = std::make_unique<_internal::SharedKeyPolicy>(credential); m_pipeline = std::make_shared( - _internal::BuildHttpPipelinePolicies(options, std::move(pipelineOptions))); + _detail::BuildQueuePipelinePolicies(options, std::move(pipelineOptions))); } QueueServiceClient::QueueServiceClient( @@ -75,7 +77,7 @@ namespace Azure { namespace Storage { namespace Queues { } m_pipeline = std::make_shared( - _internal::BuildHttpPipelinePolicies(options, std::move(pipelineOptions))); + _detail::BuildQueuePipelinePolicies(options, std::move(pipelineOptions))); } QueueServiceClient::QueueServiceClient( @@ -91,7 +93,7 @@ namespace Azure { namespace Storage { namespace Queues { pipelineOptions.ApiVersion = options.ApiVersion.ToString(); m_pipeline = std::make_shared( - _internal::BuildHttpPipelinePolicies(options, std::move(pipelineOptions))); + _detail::BuildQueuePipelinePolicies(options, std::move(pipelineOptions))); } QueueClient QueueServiceClient::GetQueueClient(const std::string& queueName) const @@ -105,22 +107,47 @@ namespace Azure { namespace Storage { namespace Queues { const ListQueuesOptions& options, const Azure::Core::Context& context) const { - _detail::ServiceClient::ListServiceQueuesSegmentOptions protocolLayerOptions; + _detail::QueuesClientGetQueuesServiceOptions protocolLayerOptions; protocolLayerOptions.Prefix = options.Prefix; protocolLayerOptions.Marker = options.ContinuationToken; - protocolLayerOptions.MaxResults = options.PageSizeHint; - protocolLayerOptions.Include = options.Include; - auto response = _detail::ServiceClient::ListQueuesSegment( - *m_pipeline, m_serviceUrl, protocolLayerOptions, _internal::WithReplicaStatus(context)); + protocolLayerOptions.Maxresults = options.PageSizeHint; + if ((options.Include & Models::ListQueuesIncludeFlags::Metadata) + == Models::ListQueuesIncludeFlags::Metadata) + { + protocolLayerOptions.Include = std::vector<_detail::Models::ListQueuesIncludeType>{ + _detail::Models::ListQueuesIncludeType::Metadata}; + } + auto response = _detail::InvokeStorageProtocol([&] { + return _detail::QueuesClient(m_serviceUrl, m_pipeline) + .GetQueuesService(protocolLayerOptions, _internal::WithReplicaStatus(context)); + }); ListQueuesPagedResponse pagedResponse; pagedResponse.ServiceEndpoint = std::move(response.Value.ServiceEndpoint); pagedResponse.Prefix = std::move(response.Value.Prefix); - pagedResponse.Queues = std::move(response.Value.Items); + if (response.Value.QueueItems.HasValue()) + { + pagedResponse.Queues.reserve(response.Value.QueueItems.Value().size()); + for (auto& item : response.Value.QueueItems.Value()) + { + Models::QueueItem queue; + queue.Name = std::move(item.Name); + if (item.Metadata.HasValue()) + { + for (auto& entry : item.Metadata.Value()) + { + queue.Metadata[entry.first] = std::move(entry.second); + } + } + pagedResponse.Queues.emplace_back(std::move(queue)); + } + } pagedResponse.m_queueServiceClient = std::make_shared(*this); pagedResponse.m_operationOptions = options; pagedResponse.CurrentPageToken = options.ContinuationToken.ValueOr(std::string()); - pagedResponse.NextPageToken = response.Value.ContinuationToken; + pagedResponse.NextPageToken = response.Value.NextMarker.empty() + ? Azure::Nullable() + : Azure::Nullable(std::move(response.Value.NextMarker)); pagedResponse.RawResponse = std::move(response.RawResponse); return pagedResponse; @@ -132,10 +159,12 @@ namespace Azure { namespace Storage { namespace Queues { const Azure::Core::Context& context) const { (void)options; - _detail::ServiceClient::SetServicePropertiesOptions protocolLayerOptions; - protocolLayerOptions.QueueServiceProperties = std::move(properties); - return _detail::ServiceClient::SetProperties( - *m_pipeline, m_serviceUrl, protocolLayerOptions, context); + auto protocolProperties = _detail::ToGenerated(std::move(properties)); + auto response = _detail::InvokeStorageProtocol([&] { + return _detail::QueuesClient(m_serviceUrl, m_pipeline) + .SetPropertiesService(protocolProperties, {}, context); + }); + return Azure::Response({}, std::move(response.RawResponse)); } Azure::Response QueueServiceClient::GetProperties( @@ -143,9 +172,12 @@ namespace Azure { namespace Storage { namespace Queues { const Azure::Core::Context& context) const { (void)options; - _detail::ServiceClient::GetServicePropertiesOptions protocolLayerOptions; - return _detail::ServiceClient::GetProperties( - *m_pipeline, m_serviceUrl, protocolLayerOptions, _internal::WithReplicaStatus(context)); + auto response = _detail::InvokeStorageProtocol([&] { + return _detail::QueuesClient(m_serviceUrl, m_pipeline) + .GetPropertiesService({}, _internal::WithReplicaStatus(context)); + }); + return Azure::Response( + _detail::ToPublic(std::move(response.Value)), std::move(response.RawResponse)); } Azure::Response QueueServiceClient::GetStatistics( @@ -153,9 +185,18 @@ namespace Azure { namespace Storage { namespace Queues { const Azure::Core::Context& context) const { (void)options; - _detail::ServiceClient::GetServiceStatisticsOptions protocolLayerOptions; - return _detail::ServiceClient::GetStatistics( - *m_pipeline, m_serviceUrl, protocolLayerOptions, context); + auto response = _detail::InvokeStorageProtocol([&] { + return _detail::QueuesClient(m_serviceUrl, m_pipeline).GetStatisticsService({}, context); + }); + Models::ServiceStatistics result; + if (response.Value.GeoReplication.HasValue()) + { + auto& replication = response.Value.GeoReplication.Value(); + result.GeoReplication.Status = Models::GeoReplicationStatus(replication.Status.ToString()); + result.GeoReplication.LastSyncedOn = replication.LastSyncTime; + } + return Azure::Response( + std::move(result), std::move(response.RawResponse)); } Azure::Response QueueServiceClient::CreateQueue( @@ -182,14 +223,27 @@ namespace Azure { namespace Storage { namespace Queues { const GetUserDelegationKeyOptions& options, const Azure::Core::Context& context) const { - _detail::ServiceClient::GetServiceUserDelegationKeyOptions protocolLayerOptions; - protocolLayerOptions.KeyInfo.Start = options.StartsOn.ToString( - Azure::DateTime::DateFormat::Rfc3339, Azure::DateTime::TimeFractionFormat::Truncate); - protocolLayerOptions.KeyInfo.Expiry = expiresOn.ToString( - Azure::DateTime::DateFormat::Rfc3339, Azure::DateTime::TimeFractionFormat::Truncate); - protocolLayerOptions.KeyInfo.DelegatedUserTid = options.DelegatedUserTid; - return _detail::ServiceClient::GetUserDelegationKey( - *m_pipeline, m_serviceUrl, protocolLayerOptions, context); + _detail::Models::KeyInfo keyInfo; + keyInfo.Start = options.StartsOn.ToString( + DateTime::DateFormat::Rfc3339, DateTime::TimeFractionFormat::Truncate); + keyInfo.Expiry + = expiresOn.ToString(DateTime::DateFormat::Rfc3339, DateTime::TimeFractionFormat::Truncate); + keyInfo.DelegatedUserTid = options.DelegatedUserTid; + auto response = _detail::InvokeStorageProtocol([&] { + return _detail::QueuesClient(m_serviceUrl, m_pipeline) + .GetUserDelegationKeyService(keyInfo, {}, context); + }); + Models::UserDelegationKey result; + result.SignedObjectId = std::move(response.Value.SignedOid); + result.SignedTenantId = std::move(response.Value.SignedTid); + result.SignedStartsOn = response.Value.SignedStart; + result.SignedExpiresOn = response.Value.SignedExpiry; + result.SignedService = std::move(response.Value.SignedService); + result.SignedVersion = std::move(response.Value.SignedVersion); + result.SignedDelegatedUserTid = std::move(response.Value.SignedDelegatedUserTid); + result.Value = std::move(response.Value.Value); + return Azure::Response( + std::move(result), std::move(response.RawResponse)); } }}} // namespace Azure::Storage::Queues diff --git a/sdk/storage/azure-storage-queues/src/rest_client.cpp b/sdk/storage/azure-storage-queues/src/rest_client.cpp deleted file mode 100644 index ad7231b184..0000000000 --- a/sdk/storage/azure-storage-queues/src/rest_client.cpp +++ /dev/null @@ -1,1550 +0,0 @@ - -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -// -// Code generated by Microsoft (R) AutoRest C++ Code Generator. -// Changes may cause incorrect behavior and will be lost if the code is regenerated. -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -namespace { -std::string ListQueuesIncludeFlagsToString( - const Azure::Storage::Queues::Models::ListQueuesIncludeFlags& val) -{ - const Azure::Storage::Queues::Models::ListQueuesIncludeFlags valueList[] = { - Azure::Storage::Queues::Models::ListQueuesIncludeFlags::Metadata, - }; - const char* stringList[] = { - "metadata", - }; - std::string ret; - for (size_t i = 0; i < 1; ++i) - { - if ((val & valueList[i]) == valueList[i]) - { - if (!ret.empty()) - { - ret += ","; - } - ret += stringList[i]; - } - } - return ret; -} -} // namespace -namespace Azure { namespace Storage { namespace Queues { - namespace Models { - const GeoReplicationStatus GeoReplicationStatus::Live("live"); - const GeoReplicationStatus GeoReplicationStatus::Bootstrap("bootstrap"); - const GeoReplicationStatus GeoReplicationStatus::Unavailable("unavailable"); - } // namespace Models - namespace _detail { - Response ServiceClient::SetProperties( - Core::Http::_internal::HttpPipeline& pipeline, - const Core::Url& url, - const SetServicePropertiesOptions& options, - const Core::Context& context) - { - std::string xmlBody; - { - _internal::XmlWriter writer; - writer.Write( - _internal::XmlNode{_internal::XmlNodeType::StartTag, "StorageServiceProperties"}); - writer.Write(_internal::XmlNode{_internal::XmlNodeType::StartTag, "Logging"}); - writer.Write(_internal::XmlNode{ - _internal::XmlNodeType::StartTag, - "Version", - options.QueueServiceProperties.Logging.Version}); - writer.Write(_internal::XmlNode{ - _internal::XmlNodeType::StartTag, - "Delete", - options.QueueServiceProperties.Logging.Delete ? "true" : "false"}); - writer.Write(_internal::XmlNode{ - _internal::XmlNodeType::StartTag, - "Read", - options.QueueServiceProperties.Logging.Read ? "true" : "false"}); - writer.Write(_internal::XmlNode{ - _internal::XmlNodeType::StartTag, - "Write", - options.QueueServiceProperties.Logging.Write ? "true" : "false"}); - writer.Write(_internal::XmlNode{_internal::XmlNodeType::StartTag, "RetentionPolicy"}); - writer.Write(_internal::XmlNode{ - _internal::XmlNodeType::StartTag, - "Enabled", - options.QueueServiceProperties.Logging.RetentionPolicy.IsEnabled ? "true" : "false"}); - if (options.QueueServiceProperties.Logging.RetentionPolicy.Days.HasValue()) - { - writer.Write(_internal::XmlNode{ - _internal::XmlNodeType::StartTag, - "Days", - std::to_string(options.QueueServiceProperties.Logging.RetentionPolicy.Days.Value())}); - } - writer.Write(_internal::XmlNode{_internal::XmlNodeType::EndTag}); - writer.Write(_internal::XmlNode{_internal::XmlNodeType::EndTag}); - writer.Write(_internal::XmlNode{_internal::XmlNodeType::StartTag, "HourMetrics"}); - writer.Write(_internal::XmlNode{ - _internal::XmlNodeType::StartTag, - "Version", - options.QueueServiceProperties.HourMetrics.Version}); - writer.Write(_internal::XmlNode{ - _internal::XmlNodeType::StartTag, - "Enabled", - options.QueueServiceProperties.HourMetrics.IsEnabled ? "true" : "false"}); - if (options.QueueServiceProperties.HourMetrics.IncludeApis.HasValue()) - { - writer.Write(_internal::XmlNode{ - _internal::XmlNodeType::StartTag, - "IncludeAPIs", - options.QueueServiceProperties.HourMetrics.IncludeApis.Value() ? "true" : "false"}); - } - writer.Write(_internal::XmlNode{_internal::XmlNodeType::StartTag, "RetentionPolicy"}); - writer.Write(_internal::XmlNode{ - _internal::XmlNodeType::StartTag, - "Enabled", - options.QueueServiceProperties.HourMetrics.RetentionPolicy.IsEnabled ? "true" - : "false"}); - if (options.QueueServiceProperties.HourMetrics.RetentionPolicy.Days.HasValue()) - { - writer.Write(_internal::XmlNode{ - _internal::XmlNodeType::StartTag, - "Days", - std::to_string( - options.QueueServiceProperties.HourMetrics.RetentionPolicy.Days.Value())}); - } - writer.Write(_internal::XmlNode{_internal::XmlNodeType::EndTag}); - writer.Write(_internal::XmlNode{_internal::XmlNodeType::EndTag}); - writer.Write(_internal::XmlNode{_internal::XmlNodeType::StartTag, "MinuteMetrics"}); - writer.Write(_internal::XmlNode{ - _internal::XmlNodeType::StartTag, - "Version", - options.QueueServiceProperties.MinuteMetrics.Version}); - writer.Write(_internal::XmlNode{ - _internal::XmlNodeType::StartTag, - "Enabled", - options.QueueServiceProperties.MinuteMetrics.IsEnabled ? "true" : "false"}); - if (options.QueueServiceProperties.MinuteMetrics.IncludeApis.HasValue()) - { - writer.Write(_internal::XmlNode{ - _internal::XmlNodeType::StartTag, - "IncludeAPIs", - options.QueueServiceProperties.MinuteMetrics.IncludeApis.Value() ? "true" : "false"}); - } - writer.Write(_internal::XmlNode{_internal::XmlNodeType::StartTag, "RetentionPolicy"}); - writer.Write(_internal::XmlNode{ - _internal::XmlNodeType::StartTag, - "Enabled", - options.QueueServiceProperties.MinuteMetrics.RetentionPolicy.IsEnabled ? "true" - : "false"}); - if (options.QueueServiceProperties.MinuteMetrics.RetentionPolicy.Days.HasValue()) - { - writer.Write(_internal::XmlNode{ - _internal::XmlNodeType::StartTag, - "Days", - std::to_string( - options.QueueServiceProperties.MinuteMetrics.RetentionPolicy.Days.Value())}); - } - writer.Write(_internal::XmlNode{_internal::XmlNodeType::EndTag}); - writer.Write(_internal::XmlNode{_internal::XmlNodeType::EndTag}); - writer.Write(_internal::XmlNode{_internal::XmlNodeType::StartTag, "Cors"}); - for (const auto& i1 : options.QueueServiceProperties.Cors) - { - writer.Write(_internal::XmlNode{_internal::XmlNodeType::StartTag, "CorsRule"}); - writer.Write(_internal::XmlNode{ - _internal::XmlNodeType::StartTag, "AllowedOrigins", i1.AllowedOrigins}); - writer.Write(_internal::XmlNode{ - _internal::XmlNodeType::StartTag, "AllowedMethods", i1.AllowedMethods}); - writer.Write(_internal::XmlNode{ - _internal::XmlNodeType::StartTag, "AllowedHeaders", i1.AllowedHeaders}); - writer.Write(_internal::XmlNode{ - _internal::XmlNodeType::StartTag, "ExposedHeaders", i1.ExposedHeaders}); - writer.Write(_internal::XmlNode{ - _internal::XmlNodeType::StartTag, - "MaxAgeInSeconds", - std::to_string(i1.MaxAgeInSeconds)}); - writer.Write(_internal::XmlNode{_internal::XmlNodeType::EndTag}); - } - writer.Write(_internal::XmlNode{_internal::XmlNodeType::EndTag}); - writer.Write(_internal::XmlNode{_internal::XmlNodeType::EndTag}); - writer.Write(_internal::XmlNode{_internal::XmlNodeType::End}); - xmlBody = writer.GetDocument(); - } - Core::IO::MemoryBodyStream requestBody( - reinterpret_cast(xmlBody.data()), xmlBody.length()); - auto request = Core::Http::Request(Core::Http::HttpMethod::Put, url, &requestBody); - request.SetHeader("Content-Type", "application/xml; charset=UTF-8"); - request.SetHeader("Content-Length", std::to_string(requestBody.Length())); - request.GetUrl().AppendQueryParameter("restype", "service"); - request.GetUrl().AppendQueryParameter("comp", "properties"); - request.SetHeader("x-ms-version", "2026-04-06"); - auto pRawResponse = pipeline.Send(request, context); - auto httpStatusCode = pRawResponse->GetStatusCode(); - if (httpStatusCode != Core::Http::HttpStatusCode::Accepted) - { - throw StorageException::CreateFromResponse(std::move(pRawResponse)); - } - Models::SetServicePropertiesResult response; - return Response( - std::move(response), std::move(pRawResponse)); - } - Response ServiceClient::GetProperties( - Core::Http::_internal::HttpPipeline& pipeline, - const Core::Url& url, - const GetServicePropertiesOptions& options, - const Core::Context& context) - { - auto request = Core::Http::Request(Core::Http::HttpMethod::Get, url); - request.GetUrl().AppendQueryParameter("restype", "service"); - request.GetUrl().AppendQueryParameter("comp", "properties"); - request.SetHeader("x-ms-version", "2026-04-06"); - (void)options; - auto pRawResponse = pipeline.Send(request, context); - auto httpStatusCode = pRawResponse->GetStatusCode(); - if (httpStatusCode != Core::Http::HttpStatusCode::Ok) - { - throw StorageException::CreateFromResponse(std::move(pRawResponse)); - } - Models::QueueServiceProperties response; - { - const auto& responseBody = pRawResponse->GetBody(); - _internal::XmlReader reader( - reinterpret_cast(responseBody.data()), responseBody.size()); - enum class XmlTagEnum - { - kUnknown, - kStorageServiceProperties, - kLogging, - kVersion, - kDelete, - kRead, - kWrite, - kRetentionPolicy, - kEnabled, - kDays, - kHourMetrics, - kIncludeAPIs, - kMinuteMetrics, - kCors, - kCorsRule, - kAllowedOrigins, - kAllowedMethods, - kAllowedHeaders, - kExposedHeaders, - kMaxAgeInSeconds, - }; - const std::unordered_map XmlTagEnumMap{ - {"StorageServiceProperties", XmlTagEnum::kStorageServiceProperties}, - {"Logging", XmlTagEnum::kLogging}, - {"Version", XmlTagEnum::kVersion}, - {"Delete", XmlTagEnum::kDelete}, - {"Read", XmlTagEnum::kRead}, - {"Write", XmlTagEnum::kWrite}, - {"RetentionPolicy", XmlTagEnum::kRetentionPolicy}, - {"Enabled", XmlTagEnum::kEnabled}, - {"Days", XmlTagEnum::kDays}, - {"HourMetrics", XmlTagEnum::kHourMetrics}, - {"IncludeAPIs", XmlTagEnum::kIncludeAPIs}, - {"MinuteMetrics", XmlTagEnum::kMinuteMetrics}, - {"Cors", XmlTagEnum::kCors}, - {"CorsRule", XmlTagEnum::kCorsRule}, - {"AllowedOrigins", XmlTagEnum::kAllowedOrigins}, - {"AllowedMethods", XmlTagEnum::kAllowedMethods}, - {"AllowedHeaders", XmlTagEnum::kAllowedHeaders}, - {"ExposedHeaders", XmlTagEnum::kExposedHeaders}, - {"MaxAgeInSeconds", XmlTagEnum::kMaxAgeInSeconds}, - }; - std::vector xmlPath; - Models::CorsRule vectorElement1; - while (true) - { - auto node = reader.Read(); - if (node.Type == _internal::XmlNodeType::End) - { - break; - } - else if (node.Type == _internal::XmlNodeType::StartTag) - { - auto ite = XmlTagEnumMap.find(node.Name); - xmlPath.push_back(ite == XmlTagEnumMap.end() ? XmlTagEnum::kUnknown : ite->second); - } - else if (node.Type == _internal::XmlNodeType::Text) - { - if (xmlPath.size() == 3 && xmlPath[0] == XmlTagEnum::kStorageServiceProperties - && xmlPath[1] == XmlTagEnum::kLogging && xmlPath[2] == XmlTagEnum::kVersion) - { - response.Logging.Version = node.Value; - } - else if ( - xmlPath.size() == 3 && xmlPath[0] == XmlTagEnum::kStorageServiceProperties - && xmlPath[1] == XmlTagEnum::kLogging && xmlPath[2] == XmlTagEnum::kDelete) - { - response.Logging.Delete = node.Value == std::string("true"); - } - else if ( - xmlPath.size() == 3 && xmlPath[0] == XmlTagEnum::kStorageServiceProperties - && xmlPath[1] == XmlTagEnum::kLogging && xmlPath[2] == XmlTagEnum::kRead) - { - response.Logging.Read = node.Value == std::string("true"); - } - else if ( - xmlPath.size() == 3 && xmlPath[0] == XmlTagEnum::kStorageServiceProperties - && xmlPath[1] == XmlTagEnum::kLogging && xmlPath[2] == XmlTagEnum::kWrite) - { - response.Logging.Write = node.Value == std::string("true"); - } - else if ( - xmlPath.size() == 4 && xmlPath[0] == XmlTagEnum::kStorageServiceProperties - && xmlPath[1] == XmlTagEnum::kLogging && xmlPath[2] == XmlTagEnum::kRetentionPolicy - && xmlPath[3] == XmlTagEnum::kEnabled) - { - response.Logging.RetentionPolicy.IsEnabled = node.Value == std::string("true"); - } - else if ( - xmlPath.size() == 4 && xmlPath[0] == XmlTagEnum::kStorageServiceProperties - && xmlPath[1] == XmlTagEnum::kLogging && xmlPath[2] == XmlTagEnum::kRetentionPolicy - && xmlPath[3] == XmlTagEnum::kDays) - { - response.Logging.RetentionPolicy.Days = std::stoi(node.Value); - } - else if ( - xmlPath.size() == 3 && xmlPath[0] == XmlTagEnum::kStorageServiceProperties - && xmlPath[1] == XmlTagEnum::kHourMetrics && xmlPath[2] == XmlTagEnum::kVersion) - { - response.HourMetrics.Version = node.Value; - } - else if ( - xmlPath.size() == 3 && xmlPath[0] == XmlTagEnum::kStorageServiceProperties - && xmlPath[1] == XmlTagEnum::kHourMetrics && xmlPath[2] == XmlTagEnum::kEnabled) - { - response.HourMetrics.IsEnabled = node.Value == std::string("true"); - } - else if ( - xmlPath.size() == 3 && xmlPath[0] == XmlTagEnum::kStorageServiceProperties - && xmlPath[1] == XmlTagEnum::kHourMetrics && xmlPath[2] == XmlTagEnum::kIncludeAPIs) - { - response.HourMetrics.IncludeApis = node.Value == std::string("true"); - } - else if ( - xmlPath.size() == 4 && xmlPath[0] == XmlTagEnum::kStorageServiceProperties - && xmlPath[1] == XmlTagEnum::kHourMetrics - && xmlPath[2] == XmlTagEnum::kRetentionPolicy && xmlPath[3] == XmlTagEnum::kEnabled) - { - response.HourMetrics.RetentionPolicy.IsEnabled = node.Value == std::string("true"); - } - else if ( - xmlPath.size() == 4 && xmlPath[0] == XmlTagEnum::kStorageServiceProperties - && xmlPath[1] == XmlTagEnum::kHourMetrics - && xmlPath[2] == XmlTagEnum::kRetentionPolicy && xmlPath[3] == XmlTagEnum::kDays) - { - response.HourMetrics.RetentionPolicy.Days = std::stoi(node.Value); - } - else if ( - xmlPath.size() == 3 && xmlPath[0] == XmlTagEnum::kStorageServiceProperties - && xmlPath[1] == XmlTagEnum::kMinuteMetrics && xmlPath[2] == XmlTagEnum::kVersion) - { - response.MinuteMetrics.Version = node.Value; - } - else if ( - xmlPath.size() == 3 && xmlPath[0] == XmlTagEnum::kStorageServiceProperties - && xmlPath[1] == XmlTagEnum::kMinuteMetrics && xmlPath[2] == XmlTagEnum::kEnabled) - { - response.MinuteMetrics.IsEnabled = node.Value == std::string("true"); - } - else if ( - xmlPath.size() == 3 && xmlPath[0] == XmlTagEnum::kStorageServiceProperties - && xmlPath[1] == XmlTagEnum::kMinuteMetrics - && xmlPath[2] == XmlTagEnum::kIncludeAPIs) - { - response.MinuteMetrics.IncludeApis = node.Value == std::string("true"); - } - else if ( - xmlPath.size() == 4 && xmlPath[0] == XmlTagEnum::kStorageServiceProperties - && xmlPath[1] == XmlTagEnum::kMinuteMetrics - && xmlPath[2] == XmlTagEnum::kRetentionPolicy && xmlPath[3] == XmlTagEnum::kEnabled) - { - response.MinuteMetrics.RetentionPolicy.IsEnabled = node.Value == std::string("true"); - } - else if ( - xmlPath.size() == 4 && xmlPath[0] == XmlTagEnum::kStorageServiceProperties - && xmlPath[1] == XmlTagEnum::kMinuteMetrics - && xmlPath[2] == XmlTagEnum::kRetentionPolicy && xmlPath[3] == XmlTagEnum::kDays) - { - response.MinuteMetrics.RetentionPolicy.Days = std::stoi(node.Value); - } - else if ( - xmlPath.size() == 4 && xmlPath[0] == XmlTagEnum::kStorageServiceProperties - && xmlPath[1] == XmlTagEnum::kCors && xmlPath[2] == XmlTagEnum::kCorsRule - && xmlPath[3] == XmlTagEnum::kAllowedOrigins) - { - vectorElement1.AllowedOrigins = node.Value; - } - else if ( - xmlPath.size() == 4 && xmlPath[0] == XmlTagEnum::kStorageServiceProperties - && xmlPath[1] == XmlTagEnum::kCors && xmlPath[2] == XmlTagEnum::kCorsRule - && xmlPath[3] == XmlTagEnum::kAllowedMethods) - { - vectorElement1.AllowedMethods = node.Value; - } - else if ( - xmlPath.size() == 4 && xmlPath[0] == XmlTagEnum::kStorageServiceProperties - && xmlPath[1] == XmlTagEnum::kCors && xmlPath[2] == XmlTagEnum::kCorsRule - && xmlPath[3] == XmlTagEnum::kAllowedHeaders) - { - vectorElement1.AllowedHeaders = node.Value; - } - else if ( - xmlPath.size() == 4 && xmlPath[0] == XmlTagEnum::kStorageServiceProperties - && xmlPath[1] == XmlTagEnum::kCors && xmlPath[2] == XmlTagEnum::kCorsRule - && xmlPath[3] == XmlTagEnum::kExposedHeaders) - { - vectorElement1.ExposedHeaders = node.Value; - } - else if ( - xmlPath.size() == 4 && xmlPath[0] == XmlTagEnum::kStorageServiceProperties - && xmlPath[1] == XmlTagEnum::kCors && xmlPath[2] == XmlTagEnum::kCorsRule - && xmlPath[3] == XmlTagEnum::kMaxAgeInSeconds) - { - vectorElement1.MaxAgeInSeconds = std::stoi(node.Value); - } - } - else if (node.Type == _internal::XmlNodeType::Attribute) - { - } - else if (node.Type == _internal::XmlNodeType::EndTag) - { - if (xmlPath.size() == 3 && xmlPath[0] == XmlTagEnum::kStorageServiceProperties - && xmlPath[1] == XmlTagEnum::kCors && xmlPath[2] == XmlTagEnum::kCorsRule) - { - response.Cors.push_back(std::move(vectorElement1)); - vectorElement1 = Models::CorsRule(); - } - xmlPath.pop_back(); - } - } - } - return Response(std::move(response), std::move(pRawResponse)); - } - Response ServiceClient::GetStatistics( - Core::Http::_internal::HttpPipeline& pipeline, - const Core::Url& url, - const GetServiceStatisticsOptions& options, - const Core::Context& context) - { - auto request = Core::Http::Request(Core::Http::HttpMethod::Get, url); - request.GetUrl().AppendQueryParameter("restype", "service"); - request.GetUrl().AppendQueryParameter("comp", "stats"); - request.SetHeader("x-ms-version", "2026-04-06"); - (void)options; - auto pRawResponse = pipeline.Send(request, context); - auto httpStatusCode = pRawResponse->GetStatusCode(); - if (httpStatusCode != Core::Http::HttpStatusCode::Ok) - { - throw StorageException::CreateFromResponse(std::move(pRawResponse)); - } - Models::ServiceStatistics response; - { - const auto& responseBody = pRawResponse->GetBody(); - _internal::XmlReader reader( - reinterpret_cast(responseBody.data()), responseBody.size()); - enum class XmlTagEnum - { - kUnknown, - kStorageServiceStats, - kGeoReplication, - kStatus, - kLastSyncTime, - }; - const std::unordered_map XmlTagEnumMap{ - {"StorageServiceStats", XmlTagEnum::kStorageServiceStats}, - {"GeoReplication", XmlTagEnum::kGeoReplication}, - {"Status", XmlTagEnum::kStatus}, - {"LastSyncTime", XmlTagEnum::kLastSyncTime}, - }; - std::vector xmlPath; - - while (true) - { - auto node = reader.Read(); - if (node.Type == _internal::XmlNodeType::End) - { - break; - } - else if (node.Type == _internal::XmlNodeType::StartTag) - { - auto ite = XmlTagEnumMap.find(node.Name); - xmlPath.push_back(ite == XmlTagEnumMap.end() ? XmlTagEnum::kUnknown : ite->second); - } - else if (node.Type == _internal::XmlNodeType::Text) - { - if (xmlPath.size() == 3 && xmlPath[0] == XmlTagEnum::kStorageServiceStats - && xmlPath[1] == XmlTagEnum::kGeoReplication && xmlPath[2] == XmlTagEnum::kStatus) - { - response.GeoReplication.Status = Models::GeoReplicationStatus(node.Value); - } - else if ( - xmlPath.size() == 3 && xmlPath[0] == XmlTagEnum::kStorageServiceStats - && xmlPath[1] == XmlTagEnum::kGeoReplication - && xmlPath[2] == XmlTagEnum::kLastSyncTime) - { - response.GeoReplication.LastSyncedOn - = DateTime::Parse(node.Value, Azure::DateTime::DateFormat::Rfc1123); - } - } - else if (node.Type == _internal::XmlNodeType::Attribute) - { - } - else if (node.Type == _internal::XmlNodeType::EndTag) - { - - xmlPath.pop_back(); - } - } - } - return Response(std::move(response), std::move(pRawResponse)); - } - Response ServiceClient::GetUserDelegationKey( - Core::Http::_internal::HttpPipeline& pipeline, - const Core::Url& url, - const GetServiceUserDelegationKeyOptions& options, - const Core::Context& context) - { - std::string xmlBody; - { - _internal::XmlWriter writer; - writer.Write(_internal::XmlNode{_internal::XmlNodeType::StartTag, "KeyInfo"}); - if (options.KeyInfo.Start.HasValue()) - { - writer.Write(_internal::XmlNode{ - _internal::XmlNodeType::StartTag, "Start", options.KeyInfo.Start.Value()}); - } - writer.Write( - _internal::XmlNode{_internal::XmlNodeType::StartTag, "Expiry", options.KeyInfo.Expiry}); - if (options.KeyInfo.DelegatedUserTid.HasValue()) - { - writer.Write(_internal::XmlNode{ - _internal::XmlNodeType::StartTag, - "DelegatedUserTid", - options.KeyInfo.DelegatedUserTid.Value()}); - } - writer.Write(_internal::XmlNode{_internal::XmlNodeType::EndTag}); - writer.Write(_internal::XmlNode{_internal::XmlNodeType::End}); - xmlBody = writer.GetDocument(); - } - Core::IO::MemoryBodyStream requestBody( - reinterpret_cast(xmlBody.data()), xmlBody.length()); - auto request = Core::Http::Request(Core::Http::HttpMethod::Post, url, &requestBody); - request.SetHeader("Content-Type", "application/xml; charset=UTF-8"); - request.SetHeader("Content-Length", std::to_string(requestBody.Length())); - request.GetUrl().AppendQueryParameter("restype", "service"); - request.GetUrl().AppendQueryParameter("comp", "userdelegationkey"); - request.SetHeader("x-ms-version", "2026-04-06"); - auto pRawResponse = pipeline.Send(request, context); - auto httpStatusCode = pRawResponse->GetStatusCode(); - if (httpStatusCode != Core::Http::HttpStatusCode::Ok) - { - throw StorageException::CreateFromResponse(std::move(pRawResponse)); - } - Models::UserDelegationKey response; - { - const auto& responseBody = pRawResponse->GetBody(); - _internal::XmlReader reader( - reinterpret_cast(responseBody.data()), responseBody.size()); - enum class XmlTagEnum - { - kUnknown, - kUserDelegationKey, - kSignedOid, - kSignedTid, - kSignedStart, - kSignedExpiry, - kSignedService, - kSignedVersion, - kSignedDelegatedUserTid, - kValue, - }; - const std::unordered_map XmlTagEnumMap{ - {"UserDelegationKey", XmlTagEnum::kUserDelegationKey}, - {"SignedOid", XmlTagEnum::kSignedOid}, - {"SignedTid", XmlTagEnum::kSignedTid}, - {"SignedStart", XmlTagEnum::kSignedStart}, - {"SignedExpiry", XmlTagEnum::kSignedExpiry}, - {"SignedService", XmlTagEnum::kSignedService}, - {"SignedVersion", XmlTagEnum::kSignedVersion}, - {"SignedDelegatedUserTid", XmlTagEnum::kSignedDelegatedUserTid}, - {"Value", XmlTagEnum::kValue}, - }; - std::vector xmlPath; - - while (true) - { - auto node = reader.Read(); - if (node.Type == _internal::XmlNodeType::End) - { - break; - } - else if (node.Type == _internal::XmlNodeType::StartTag) - { - auto ite = XmlTagEnumMap.find(node.Name); - xmlPath.push_back(ite == XmlTagEnumMap.end() ? XmlTagEnum::kUnknown : ite->second); - } - else if (node.Type == _internal::XmlNodeType::Text) - { - if (xmlPath.size() == 2 && xmlPath[0] == XmlTagEnum::kUserDelegationKey - && xmlPath[1] == XmlTagEnum::kSignedOid) - { - response.SignedObjectId = node.Value; - } - else if ( - xmlPath.size() == 2 && xmlPath[0] == XmlTagEnum::kUserDelegationKey - && xmlPath[1] == XmlTagEnum::kSignedTid) - { - response.SignedTenantId = node.Value; - } - else if ( - xmlPath.size() == 2 && xmlPath[0] == XmlTagEnum::kUserDelegationKey - && xmlPath[1] == XmlTagEnum::kSignedStart) - { - response.SignedStartsOn - = DateTime::Parse(node.Value, Azure::DateTime::DateFormat::Rfc3339); - } - else if ( - xmlPath.size() == 2 && xmlPath[0] == XmlTagEnum::kUserDelegationKey - && xmlPath[1] == XmlTagEnum::kSignedExpiry) - { - response.SignedExpiresOn - = DateTime::Parse(node.Value, Azure::DateTime::DateFormat::Rfc3339); - } - else if ( - xmlPath.size() == 2 && xmlPath[0] == XmlTagEnum::kUserDelegationKey - && xmlPath[1] == XmlTagEnum::kSignedService) - { - response.SignedService = node.Value; - } - else if ( - xmlPath.size() == 2 && xmlPath[0] == XmlTagEnum::kUserDelegationKey - && xmlPath[1] == XmlTagEnum::kSignedVersion) - { - response.SignedVersion = node.Value; - } - else if ( - xmlPath.size() == 2 && xmlPath[0] == XmlTagEnum::kUserDelegationKey - && xmlPath[1] == XmlTagEnum::kSignedDelegatedUserTid) - { - response.SignedDelegatedUserTid = node.Value; - } - else if ( - xmlPath.size() == 2 && xmlPath[0] == XmlTagEnum::kUserDelegationKey - && xmlPath[1] == XmlTagEnum::kValue) - { - response.Value = node.Value; - } - } - else if (node.Type == _internal::XmlNodeType::Attribute) - { - } - else if (node.Type == _internal::XmlNodeType::EndTag) - { - - xmlPath.pop_back(); - } - } - } - return Response(std::move(response), std::move(pRawResponse)); - } - Response ServiceClient::ListQueuesSegment( - Core::Http::_internal::HttpPipeline& pipeline, - const Core::Url& url, - const ListServiceQueuesSegmentOptions& options, - const Core::Context& context) - { - auto request = Core::Http::Request(Core::Http::HttpMethod::Get, url); - request.GetUrl().AppendQueryParameter("comp", "list"); - if (options.Prefix.HasValue() && !options.Prefix.Value().empty()) - { - request.GetUrl().AppendQueryParameter( - "prefix", _internal::UrlEncodeQueryParameter(options.Prefix.Value())); - } - if (options.Marker.HasValue() && !options.Marker.Value().empty()) - { - request.GetUrl().AppendQueryParameter( - "marker", _internal::UrlEncodeQueryParameter(options.Marker.Value())); - } - if (options.MaxResults.HasValue()) - { - request.GetUrl().AppendQueryParameter( - "maxresults", std::to_string(options.MaxResults.Value())); - } - if (options.Include.HasValue() - && !ListQueuesIncludeFlagsToString(options.Include.Value()).empty()) - { - request.GetUrl().AppendQueryParameter( - "include", - _internal::UrlEncodeQueryParameter( - ListQueuesIncludeFlagsToString(options.Include.Value()))); - } - request.SetHeader("x-ms-version", "2026-04-06"); - auto pRawResponse = pipeline.Send(request, context); - auto httpStatusCode = pRawResponse->GetStatusCode(); - if (httpStatusCode != Core::Http::HttpStatusCode::Ok) - { - throw StorageException::CreateFromResponse(std::move(pRawResponse)); - } - Models::_detail::ListQueuesResult response; - { - const auto& responseBody = pRawResponse->GetBody(); - _internal::XmlReader reader( - reinterpret_cast(responseBody.data()), responseBody.size()); - enum class XmlTagEnum - { - kUnknown, - kEnumerationResults, - kPrefix, - kQueues, - kQueue, - kName, - kMetadata, - kNextMarker, - }; - const std::unordered_map XmlTagEnumMap{ - {"EnumerationResults", XmlTagEnum::kEnumerationResults}, - {"Prefix", XmlTagEnum::kPrefix}, - {"Queues", XmlTagEnum::kQueues}, - {"Queue", XmlTagEnum::kQueue}, - {"Name", XmlTagEnum::kName}, - {"Metadata", XmlTagEnum::kMetadata}, - {"NextMarker", XmlTagEnum::kNextMarker}, - }; - std::vector xmlPath; - Models::QueueItem vectorElement1; - std::string mapKey2; - std::string mapValue3; - while (true) - { - auto node = reader.Read(); - if (node.Type == _internal::XmlNodeType::End) - { - break; - } - else if (node.Type == _internal::XmlNodeType::StartTag) - { - auto ite = XmlTagEnumMap.find(node.Name); - xmlPath.push_back(ite == XmlTagEnumMap.end() ? XmlTagEnum::kUnknown : ite->second); - if (xmlPath.size() == 5 && xmlPath[0] == XmlTagEnum::kEnumerationResults - && xmlPath[1] == XmlTagEnum::kQueues && xmlPath[2] == XmlTagEnum::kQueue - && xmlPath[3] == XmlTagEnum::kMetadata) - { - mapKey2 = node.Name; - } - } - else if (node.Type == _internal::XmlNodeType::Text) - { - if (xmlPath.size() == 2 && xmlPath[0] == XmlTagEnum::kEnumerationResults - && xmlPath[1] == XmlTagEnum::kPrefix) - { - response.Prefix = node.Value; - } - else if ( - xmlPath.size() == 4 && xmlPath[0] == XmlTagEnum::kEnumerationResults - && xmlPath[1] == XmlTagEnum::kQueues && xmlPath[2] == XmlTagEnum::kQueue - && xmlPath[3] == XmlTagEnum::kName) - { - vectorElement1.Name = node.Value; - } - else if ( - xmlPath.size() == 5 && xmlPath[0] == XmlTagEnum::kEnumerationResults - && xmlPath[1] == XmlTagEnum::kQueues && xmlPath[2] == XmlTagEnum::kQueue - && xmlPath[3] == XmlTagEnum::kMetadata) - { - mapValue3 = node.Value; - } - else if ( - xmlPath.size() == 2 && xmlPath[0] == XmlTagEnum::kEnumerationResults - && xmlPath[1] == XmlTagEnum::kNextMarker) - { - response.ContinuationToken = node.Value; - } - } - else if (node.Type == _internal::XmlNodeType::Attribute) - { - if (xmlPath.size() == 1 && xmlPath[0] == XmlTagEnum::kEnumerationResults - && node.Name == "ServiceEndpoint") - { - response.ServiceEndpoint = node.Value; - } - } - else if (node.Type == _internal::XmlNodeType::EndTag) - { - if (xmlPath.size() == 5 && xmlPath[0] == XmlTagEnum::kEnumerationResults - && xmlPath[1] == XmlTagEnum::kQueues && xmlPath[2] == XmlTagEnum::kQueue - && xmlPath[3] == XmlTagEnum::kMetadata) - { - vectorElement1.Metadata[std::move(mapKey2)] = std::move(mapValue3); - } - else if ( - xmlPath.size() == 3 && xmlPath[0] == XmlTagEnum::kEnumerationResults - && xmlPath[1] == XmlTagEnum::kQueues && xmlPath[2] == XmlTagEnum::kQueue) - { - response.Items.push_back(std::move(vectorElement1)); - vectorElement1 = Models::QueueItem(); - } - xmlPath.pop_back(); - } - } - } - return Response( - std::move(response), std::move(pRawResponse)); - } - Response QueueClient::Create( - Core::Http::_internal::HttpPipeline& pipeline, - const Core::Url& url, - const CreateQueueOptions& options, - const Core::Context& context) - { - auto request = Core::Http::Request(Core::Http::HttpMethod::Put, url); - for (const auto& p : options.Metadata) - { - request.SetHeader("x-ms-meta-" + p.first, p.second); - } - request.SetHeader("x-ms-version", "2026-04-06"); - auto pRawResponse = pipeline.Send(request, context); - auto httpStatusCode = pRawResponse->GetStatusCode(); - if (!(httpStatusCode == Core::Http::HttpStatusCode::Created - || httpStatusCode == Core::Http::HttpStatusCode::NoContent)) - { - throw StorageException::CreateFromResponse(std::move(pRawResponse)); - } - Models::CreateQueueResult response; - return Response(std::move(response), std::move(pRawResponse)); - } - Response QueueClient::Delete( - Core::Http::_internal::HttpPipeline& pipeline, - const Core::Url& url, - const DeleteQueueOptions& options, - const Core::Context& context) - { - auto request = Core::Http::Request(Core::Http::HttpMethod::Delete, url); - request.SetHeader("x-ms-version", "2026-04-06"); - (void)options; - auto pRawResponse = pipeline.Send(request, context); - auto httpStatusCode = pRawResponse->GetStatusCode(); - if (httpStatusCode != Core::Http::HttpStatusCode::NoContent) - { - throw StorageException::CreateFromResponse(std::move(pRawResponse)); - } - Models::DeleteQueueResult response; - return Response(std::move(response), std::move(pRawResponse)); - } - Response QueueClient::GetProperties( - Core::Http::_internal::HttpPipeline& pipeline, - const Core::Url& url, - const GetQueuePropertiesOptions& options, - const Core::Context& context) - { - auto request = Core::Http::Request(Core::Http::HttpMethod::Get, url); - request.GetUrl().AppendQueryParameter("comp", "metadata"); - request.SetHeader("x-ms-version", "2026-04-06"); - (void)options; - auto pRawResponse = pipeline.Send(request, context); - auto httpStatusCode = pRawResponse->GetStatusCode(); - if (httpStatusCode != Core::Http::HttpStatusCode::Ok) - { - throw StorageException::CreateFromResponse(std::move(pRawResponse)); - } - Models::_detail::QueueProperties response; - for (auto i = pRawResponse->GetHeaders().lower_bound("x-ms-meta-"); - i != pRawResponse->GetHeaders().end() && i->first.substr(0, 10) == "x-ms-meta-"; - ++i) - { - response.Metadata.emplace(i->first.substr(10), i->second); - } - response.ApproximateMessageCount - = std::stoll(pRawResponse->GetHeaders().at("x-ms-approximate-messages-count")); - return Response( - std::move(response), std::move(pRawResponse)); - } - Response QueueClient::SetMetadata( - Core::Http::_internal::HttpPipeline& pipeline, - const Core::Url& url, - const SetQueueMetadataOptions& options, - const Core::Context& context) - { - auto request = Core::Http::Request(Core::Http::HttpMethod::Put, url); - request.GetUrl().AppendQueryParameter("comp", "metadata"); - for (const auto& p : options.Metadata) - { - request.SetHeader("x-ms-meta-" + p.first, p.second); - } - request.SetHeader("x-ms-version", "2026-04-06"); - auto pRawResponse = pipeline.Send(request, context); - auto httpStatusCode = pRawResponse->GetStatusCode(); - if (httpStatusCode != Core::Http::HttpStatusCode::NoContent) - { - throw StorageException::CreateFromResponse(std::move(pRawResponse)); - } - Models::SetQueueMetadataResult response; - return Response(std::move(response), std::move(pRawResponse)); - } - Response QueueClient::GetAccessPolicy( - Core::Http::_internal::HttpPipeline& pipeline, - const Core::Url& url, - const GetQueueAccessPolicyOptions& options, - const Core::Context& context) - { - auto request = Core::Http::Request(Core::Http::HttpMethod::Get, url); - request.GetUrl().AppendQueryParameter("comp", "acl"); - request.SetHeader("x-ms-version", "2026-04-06"); - (void)options; - auto pRawResponse = pipeline.Send(request, context); - auto httpStatusCode = pRawResponse->GetStatusCode(); - if (httpStatusCode != Core::Http::HttpStatusCode::Ok) - { - throw StorageException::CreateFromResponse(std::move(pRawResponse)); - } - Models::QueueAccessPolicy response; - { - const auto& responseBody = pRawResponse->GetBody(); - _internal::XmlReader reader( - reinterpret_cast(responseBody.data()), responseBody.size()); - enum class XmlTagEnum - { - kUnknown, - kSignedIdentifiers, - kSignedIdentifier, - kId, - kAccessPolicy, - kStart, - kExpiry, - kPermission, - }; - const std::unordered_map XmlTagEnumMap{ - {"SignedIdentifiers", XmlTagEnum::kSignedIdentifiers}, - {"SignedIdentifier", XmlTagEnum::kSignedIdentifier}, - {"Id", XmlTagEnum::kId}, - {"AccessPolicy", XmlTagEnum::kAccessPolicy}, - {"Start", XmlTagEnum::kStart}, - {"Expiry", XmlTagEnum::kExpiry}, - {"Permission", XmlTagEnum::kPermission}, - }; - std::vector xmlPath; - Models::SignedIdentifier vectorElement1; - while (true) - { - auto node = reader.Read(); - if (node.Type == _internal::XmlNodeType::End) - { - break; - } - else if (node.Type == _internal::XmlNodeType::StartTag) - { - auto ite = XmlTagEnumMap.find(node.Name); - xmlPath.push_back(ite == XmlTagEnumMap.end() ? XmlTagEnum::kUnknown : ite->second); - } - else if (node.Type == _internal::XmlNodeType::Text) - { - if (xmlPath.size() == 3 && xmlPath[0] == XmlTagEnum::kSignedIdentifiers - && xmlPath[1] == XmlTagEnum::kSignedIdentifier && xmlPath[2] == XmlTagEnum::kId) - { - vectorElement1.Id = node.Value; - } - else if ( - xmlPath.size() == 4 && xmlPath[0] == XmlTagEnum::kSignedIdentifiers - && xmlPath[1] == XmlTagEnum::kSignedIdentifier - && xmlPath[2] == XmlTagEnum::kAccessPolicy && xmlPath[3] == XmlTagEnum::kStart) - { - vectorElement1.StartsOn - = DateTime::Parse(node.Value, Azure::DateTime::DateFormat::Rfc3339); - } - else if ( - xmlPath.size() == 4 && xmlPath[0] == XmlTagEnum::kSignedIdentifiers - && xmlPath[1] == XmlTagEnum::kSignedIdentifier - && xmlPath[2] == XmlTagEnum::kAccessPolicy && xmlPath[3] == XmlTagEnum::kExpiry) - { - vectorElement1.ExpiresOn - = DateTime::Parse(node.Value, Azure::DateTime::DateFormat::Rfc3339); - } - else if ( - xmlPath.size() == 4 && xmlPath[0] == XmlTagEnum::kSignedIdentifiers - && xmlPath[1] == XmlTagEnum::kSignedIdentifier - && xmlPath[2] == XmlTagEnum::kAccessPolicy && xmlPath[3] == XmlTagEnum::kPermission) - { - vectorElement1.Permissions = node.Value; - } - } - else if (node.Type == _internal::XmlNodeType::Attribute) - { - } - else if (node.Type == _internal::XmlNodeType::EndTag) - { - if (xmlPath.size() == 2 && xmlPath[0] == XmlTagEnum::kSignedIdentifiers - && xmlPath[1] == XmlTagEnum::kSignedIdentifier) - { - response.SignedIdentifiers.push_back(std::move(vectorElement1)); - vectorElement1 = Models::SignedIdentifier(); - } - xmlPath.pop_back(); - } - } - } - return Response(std::move(response), std::move(pRawResponse)); - } - Response QueueClient::SetAccessPolicy( - Core::Http::_internal::HttpPipeline& pipeline, - const Core::Url& url, - const SetQueueAccessPolicyOptions& options, - const Core::Context& context) - { - std::string xmlBody; - { - _internal::XmlWriter writer; - writer.Write(_internal::XmlNode{_internal::XmlNodeType::StartTag, "SignedIdentifiers"}); - for (const auto& i1 : options.QueueAcl) - { - writer.Write(_internal::XmlNode{_internal::XmlNodeType::StartTag, "SignedIdentifier"}); - writer.Write(_internal::XmlNode{_internal::XmlNodeType::StartTag, "Id", i1.Id}); - writer.Write(_internal::XmlNode{_internal::XmlNodeType::StartTag, "AccessPolicy"}); - if (i1.StartsOn.HasValue()) - { - writer.Write(_internal::XmlNode{ - _internal::XmlNodeType::StartTag, - "Start", - i1.StartsOn.Value().ToString( - Azure::DateTime::DateFormat::Rfc3339, - Azure::DateTime::TimeFractionFormat::AllDigits)}); - } - if (i1.ExpiresOn.HasValue()) - { - writer.Write(_internal::XmlNode{ - _internal::XmlNodeType::StartTag, - "Expiry", - i1.ExpiresOn.Value().ToString( - Azure::DateTime::DateFormat::Rfc3339, - Azure::DateTime::TimeFractionFormat::AllDigits)}); - } - writer.Write( - _internal::XmlNode{_internal::XmlNodeType::StartTag, "Permission", i1.Permissions}); - writer.Write(_internal::XmlNode{_internal::XmlNodeType::EndTag}); - writer.Write(_internal::XmlNode{_internal::XmlNodeType::EndTag}); - } - writer.Write(_internal::XmlNode{_internal::XmlNodeType::EndTag}); - writer.Write(_internal::XmlNode{_internal::XmlNodeType::End}); - xmlBody = writer.GetDocument(); - } - Core::IO::MemoryBodyStream requestBody( - reinterpret_cast(xmlBody.data()), xmlBody.length()); - auto request = Core::Http::Request(Core::Http::HttpMethod::Put, url, &requestBody); - request.SetHeader("Content-Type", "application/xml; charset=UTF-8"); - request.SetHeader("Content-Length", std::to_string(requestBody.Length())); - request.GetUrl().AppendQueryParameter("comp", "acl"); - request.SetHeader("x-ms-version", "2026-04-06"); - auto pRawResponse = pipeline.Send(request, context); - auto httpStatusCode = pRawResponse->GetStatusCode(); - if (httpStatusCode != Core::Http::HttpStatusCode::NoContent) - { - throw StorageException::CreateFromResponse(std::move(pRawResponse)); - } - Models::SetQueueAccessPolicyResult response; - return Response( - std::move(response), std::move(pRawResponse)); - } - Response QueueClient::ReceiveMessages( - Core::Http::_internal::HttpPipeline& pipeline, - const Core::Url& url, - const ReceiveQueueMessagesOptions& options, - const Core::Context& context) - { - auto request = Core::Http::Request(Core::Http::HttpMethod::Get, url); - if (options.NumberOfMessages.HasValue()) - { - request.GetUrl().AppendQueryParameter( - "numofmessages", std::to_string(options.NumberOfMessages.Value())); - } - if (options.Visibilitytimeout.HasValue()) - { - request.GetUrl().AppendQueryParameter( - "visibilitytimeout", std::to_string(options.Visibilitytimeout.Value())); - } - request.SetHeader("x-ms-version", "2026-04-06"); - auto pRawResponse = pipeline.Send(request, context); - auto httpStatusCode = pRawResponse->GetStatusCode(); - if (httpStatusCode != Core::Http::HttpStatusCode::Ok) - { - throw StorageException::CreateFromResponse(std::move(pRawResponse)); - } - Models::ReceivedMessages response; - { - const auto& responseBody = pRawResponse->GetBody(); - _internal::XmlReader reader( - reinterpret_cast(responseBody.data()), responseBody.size()); - enum class XmlTagEnum - { - kUnknown, - kQueueMessagesList, - kQueueMessage, - kMessageId, - kInsertionTime, - kExpirationTime, - kPopReceipt, - kTimeNextVisible, - kDequeueCount, - kMessageText, - }; - const std::unordered_map XmlTagEnumMap{ - {"QueueMessagesList", XmlTagEnum::kQueueMessagesList}, - {"QueueMessage", XmlTagEnum::kQueueMessage}, - {"MessageId", XmlTagEnum::kMessageId}, - {"InsertionTime", XmlTagEnum::kInsertionTime}, - {"ExpirationTime", XmlTagEnum::kExpirationTime}, - {"PopReceipt", XmlTagEnum::kPopReceipt}, - {"TimeNextVisible", XmlTagEnum::kTimeNextVisible}, - {"DequeueCount", XmlTagEnum::kDequeueCount}, - {"MessageText", XmlTagEnum::kMessageText}, - }; - std::vector xmlPath; - Models::QueueMessage vectorElement1; - while (true) - { - auto node = reader.Read(); - if (node.Type == _internal::XmlNodeType::End) - { - break; - } - else if (node.Type == _internal::XmlNodeType::StartTag) - { - auto ite = XmlTagEnumMap.find(node.Name); - xmlPath.push_back(ite == XmlTagEnumMap.end() ? XmlTagEnum::kUnknown : ite->second); - } - else if (node.Type == _internal::XmlNodeType::Text) - { - if (xmlPath.size() == 3 && xmlPath[0] == XmlTagEnum::kQueueMessagesList - && xmlPath[1] == XmlTagEnum::kQueueMessage && xmlPath[2] == XmlTagEnum::kMessageId) - { - vectorElement1.MessageId = node.Value; - } - else if ( - xmlPath.size() == 3 && xmlPath[0] == XmlTagEnum::kQueueMessagesList - && xmlPath[1] == XmlTagEnum::kQueueMessage - && xmlPath[2] == XmlTagEnum::kInsertionTime) - { - vectorElement1.InsertedOn - = DateTime::Parse(node.Value, Azure::DateTime::DateFormat::Rfc1123); - } - else if ( - xmlPath.size() == 3 && xmlPath[0] == XmlTagEnum::kQueueMessagesList - && xmlPath[1] == XmlTagEnum::kQueueMessage - && xmlPath[2] == XmlTagEnum::kExpirationTime) - { - vectorElement1.ExpiresOn - = DateTime::Parse(node.Value, Azure::DateTime::DateFormat::Rfc1123); - } - else if ( - xmlPath.size() == 3 && xmlPath[0] == XmlTagEnum::kQueueMessagesList - && xmlPath[1] == XmlTagEnum::kQueueMessage && xmlPath[2] == XmlTagEnum::kPopReceipt) - { - vectorElement1.PopReceipt = node.Value; - } - else if ( - xmlPath.size() == 3 && xmlPath[0] == XmlTagEnum::kQueueMessagesList - && xmlPath[1] == XmlTagEnum::kQueueMessage - && xmlPath[2] == XmlTagEnum::kTimeNextVisible) - { - vectorElement1.NextVisibleOn - = DateTime::Parse(node.Value, Azure::DateTime::DateFormat::Rfc1123); - } - else if ( - xmlPath.size() == 3 && xmlPath[0] == XmlTagEnum::kQueueMessagesList - && xmlPath[1] == XmlTagEnum::kQueueMessage - && xmlPath[2] == XmlTagEnum::kDequeueCount) - { - vectorElement1.DequeueCount = std::stoll(node.Value); - } - else if ( - xmlPath.size() == 3 && xmlPath[0] == XmlTagEnum::kQueueMessagesList - && xmlPath[1] == XmlTagEnum::kQueueMessage - && xmlPath[2] == XmlTagEnum::kMessageText) - { - vectorElement1.MessageText = node.Value; - } - } - else if (node.Type == _internal::XmlNodeType::Attribute) - { - } - else if (node.Type == _internal::XmlNodeType::EndTag) - { - if (xmlPath.size() == 2 && xmlPath[0] == XmlTagEnum::kQueueMessagesList - && xmlPath[1] == XmlTagEnum::kQueueMessage) - { - response.Messages.push_back(std::move(vectorElement1)); - vectorElement1 = Models::QueueMessage(); - } - xmlPath.pop_back(); - } - } - } - return Response(std::move(response), std::move(pRawResponse)); - } - Response QueueClient::ClearMessages( - Core::Http::_internal::HttpPipeline& pipeline, - const Core::Url& url, - const ClearQueueMessagesOptions& options, - const Core::Context& context) - { - auto request = Core::Http::Request(Core::Http::HttpMethod::Delete, url); - request.SetHeader("x-ms-version", "2026-04-06"); - (void)options; - auto pRawResponse = pipeline.Send(request, context); - auto httpStatusCode = pRawResponse->GetStatusCode(); - if (httpStatusCode != Core::Http::HttpStatusCode::NoContent) - { - throw StorageException::CreateFromResponse(std::move(pRawResponse)); - } - Models::ClearMessagesResult response; - return Response(std::move(response), std::move(pRawResponse)); - } - Response QueueClient::EnqueueMessage( - Core::Http::_internal::HttpPipeline& pipeline, - const Core::Url& url, - const EnqueueQueueMessageOptions& options, - const Core::Context& context) - { - std::string xmlBody; - { - _internal::XmlWriter writer; - writer.Write(_internal::XmlNode{_internal::XmlNodeType::StartTag, "QueueMessage"}); - writer.Write(_internal::XmlNode{ - _internal::XmlNodeType::StartTag, "MessageText", options.QueueMessage.MessageText}); - writer.Write(_internal::XmlNode{_internal::XmlNodeType::EndTag}); - writer.Write(_internal::XmlNode{_internal::XmlNodeType::End}); - xmlBody = writer.GetDocument(); - } - Core::IO::MemoryBodyStream requestBody( - reinterpret_cast(xmlBody.data()), xmlBody.length()); - auto request = Core::Http::Request(Core::Http::HttpMethod::Post, url, &requestBody); - request.SetHeader("Content-Type", "application/xml; charset=UTF-8"); - request.SetHeader("Content-Length", std::to_string(requestBody.Length())); - if (options.Visibilitytimeout.HasValue()) - { - request.GetUrl().AppendQueryParameter( - "visibilitytimeout", std::to_string(options.Visibilitytimeout.Value())); - } - if (options.MessageTimeToLive.HasValue()) - { - request.GetUrl().AppendQueryParameter( - "messagettl", std::to_string(options.MessageTimeToLive.Value())); - } - request.SetHeader("x-ms-version", "2026-04-06"); - auto pRawResponse = pipeline.Send(request, context); - auto httpStatusCode = pRawResponse->GetStatusCode(); - if (httpStatusCode != Core::Http::HttpStatusCode::Created) - { - throw StorageException::CreateFromResponse(std::move(pRawResponse)); - } - Models::EnqueueMessageResult response; - { - const auto& responseBody = pRawResponse->GetBody(); - _internal::XmlReader reader( - reinterpret_cast(responseBody.data()), responseBody.size()); - enum class XmlTagEnum - { - kUnknown, - kQueueMessagesList, - kQueueMessage, - kMessageId, - kInsertionTime, - kExpirationTime, - kPopReceipt, - kTimeNextVisible, - }; - const std::unordered_map XmlTagEnumMap{ - {"QueueMessagesList", XmlTagEnum::kQueueMessagesList}, - {"QueueMessage", XmlTagEnum::kQueueMessage}, - {"MessageId", XmlTagEnum::kMessageId}, - {"InsertionTime", XmlTagEnum::kInsertionTime}, - {"ExpirationTime", XmlTagEnum::kExpirationTime}, - {"PopReceipt", XmlTagEnum::kPopReceipt}, - {"TimeNextVisible", XmlTagEnum::kTimeNextVisible}, - }; - std::vector xmlPath; - - while (true) - { - auto node = reader.Read(); - if (node.Type == _internal::XmlNodeType::End) - { - break; - } - else if (node.Type == _internal::XmlNodeType::StartTag) - { - auto ite = XmlTagEnumMap.find(node.Name); - xmlPath.push_back(ite == XmlTagEnumMap.end() ? XmlTagEnum::kUnknown : ite->second); - } - else if (node.Type == _internal::XmlNodeType::Text) - { - if (xmlPath.size() == 3 && xmlPath[0] == XmlTagEnum::kQueueMessagesList - && xmlPath[1] == XmlTagEnum::kQueueMessage && xmlPath[2] == XmlTagEnum::kMessageId) - { - response.MessageId = node.Value; - } - else if ( - xmlPath.size() == 3 && xmlPath[0] == XmlTagEnum::kQueueMessagesList - && xmlPath[1] == XmlTagEnum::kQueueMessage - && xmlPath[2] == XmlTagEnum::kInsertionTime) - { - response.InsertedOn - = DateTime::Parse(node.Value, Azure::DateTime::DateFormat::Rfc1123); - } - else if ( - xmlPath.size() == 3 && xmlPath[0] == XmlTagEnum::kQueueMessagesList - && xmlPath[1] == XmlTagEnum::kQueueMessage - && xmlPath[2] == XmlTagEnum::kExpirationTime) - { - response.ExpiresOn - = DateTime::Parse(node.Value, Azure::DateTime::DateFormat::Rfc1123); - } - else if ( - xmlPath.size() == 3 && xmlPath[0] == XmlTagEnum::kQueueMessagesList - && xmlPath[1] == XmlTagEnum::kQueueMessage && xmlPath[2] == XmlTagEnum::kPopReceipt) - { - response.PopReceipt = node.Value; - } - else if ( - xmlPath.size() == 3 && xmlPath[0] == XmlTagEnum::kQueueMessagesList - && xmlPath[1] == XmlTagEnum::kQueueMessage - && xmlPath[2] == XmlTagEnum::kTimeNextVisible) - { - response.NextVisibleOn - = DateTime::Parse(node.Value, Azure::DateTime::DateFormat::Rfc1123); - } - } - else if (node.Type == _internal::XmlNodeType::Attribute) - { - } - else if (node.Type == _internal::XmlNodeType::EndTag) - { - - xmlPath.pop_back(); - } - } - } - return Response(std::move(response), std::move(pRawResponse)); - } - Response QueueClient::PeekMessages( - Core::Http::_internal::HttpPipeline& pipeline, - const Core::Url& url, - const PeekQueueMessagesOptions& options, - const Core::Context& context) - { - auto request = Core::Http::Request(Core::Http::HttpMethod::Get, url); - request.GetUrl().AppendQueryParameter("peekonly", "true"); - if (options.NumberOfMessages.HasValue()) - { - request.GetUrl().AppendQueryParameter( - "numofmessages", std::to_string(options.NumberOfMessages.Value())); - } - request.SetHeader("x-ms-version", "2026-04-06"); - auto pRawResponse = pipeline.Send(request, context); - auto httpStatusCode = pRawResponse->GetStatusCode(); - if (httpStatusCode != Core::Http::HttpStatusCode::Ok) - { - throw StorageException::CreateFromResponse(std::move(pRawResponse)); - } - Models::PeekedMessages response; - { - const auto& responseBody = pRawResponse->GetBody(); - _internal::XmlReader reader( - reinterpret_cast(responseBody.data()), responseBody.size()); - enum class XmlTagEnum - { - kUnknown, - kQueueMessagesList, - kQueueMessage, - kMessageId, - kInsertionTime, - kExpirationTime, - kDequeueCount, - kMessageText, - }; - const std::unordered_map XmlTagEnumMap{ - {"QueueMessagesList", XmlTagEnum::kQueueMessagesList}, - {"QueueMessage", XmlTagEnum::kQueueMessage}, - {"MessageId", XmlTagEnum::kMessageId}, - {"InsertionTime", XmlTagEnum::kInsertionTime}, - {"ExpirationTime", XmlTagEnum::kExpirationTime}, - {"DequeueCount", XmlTagEnum::kDequeueCount}, - {"MessageText", XmlTagEnum::kMessageText}, - }; - std::vector xmlPath; - Models::PeekedQueueMessage vectorElement1; - while (true) - { - auto node = reader.Read(); - if (node.Type == _internal::XmlNodeType::End) - { - break; - } - else if (node.Type == _internal::XmlNodeType::StartTag) - { - auto ite = XmlTagEnumMap.find(node.Name); - xmlPath.push_back(ite == XmlTagEnumMap.end() ? XmlTagEnum::kUnknown : ite->second); - } - else if (node.Type == _internal::XmlNodeType::Text) - { - if (xmlPath.size() == 3 && xmlPath[0] == XmlTagEnum::kQueueMessagesList - && xmlPath[1] == XmlTagEnum::kQueueMessage && xmlPath[2] == XmlTagEnum::kMessageId) - { - vectorElement1.MessageId = node.Value; - } - else if ( - xmlPath.size() == 3 && xmlPath[0] == XmlTagEnum::kQueueMessagesList - && xmlPath[1] == XmlTagEnum::kQueueMessage - && xmlPath[2] == XmlTagEnum::kInsertionTime) - { - vectorElement1.InsertedOn - = DateTime::Parse(node.Value, Azure::DateTime::DateFormat::Rfc1123); - } - else if ( - xmlPath.size() == 3 && xmlPath[0] == XmlTagEnum::kQueueMessagesList - && xmlPath[1] == XmlTagEnum::kQueueMessage - && xmlPath[2] == XmlTagEnum::kExpirationTime) - { - vectorElement1.ExpiresOn - = DateTime::Parse(node.Value, Azure::DateTime::DateFormat::Rfc1123); - } - else if ( - xmlPath.size() == 3 && xmlPath[0] == XmlTagEnum::kQueueMessagesList - && xmlPath[1] == XmlTagEnum::kQueueMessage - && xmlPath[2] == XmlTagEnum::kDequeueCount) - { - vectorElement1.DequeueCount = std::stoll(node.Value); - } - else if ( - xmlPath.size() == 3 && xmlPath[0] == XmlTagEnum::kQueueMessagesList - && xmlPath[1] == XmlTagEnum::kQueueMessage - && xmlPath[2] == XmlTagEnum::kMessageText) - { - vectorElement1.MessageText = node.Value; - } - } - else if (node.Type == _internal::XmlNodeType::Attribute) - { - } - else if (node.Type == _internal::XmlNodeType::EndTag) - { - if (xmlPath.size() == 2 && xmlPath[0] == XmlTagEnum::kQueueMessagesList - && xmlPath[1] == XmlTagEnum::kQueueMessage) - { - response.Messages.push_back(std::move(vectorElement1)); - vectorElement1 = Models::PeekedQueueMessage(); - } - xmlPath.pop_back(); - } - } - } - return Response(std::move(response), std::move(pRawResponse)); - } - Response QueueClient::UpdateMessage( - Core::Http::_internal::HttpPipeline& pipeline, - const Core::Url& url, - const UpdateQueueMessageOptions& options, - const Core::Context& context) - { - std::string xmlBody; - { - _internal::XmlWriter writer; - writer.Write(_internal::XmlNode{_internal::XmlNodeType::StartTag, "QueueMessage"}); - writer.Write(_internal::XmlNode{ - _internal::XmlNodeType::StartTag, "MessageText", options.QueueMessage.MessageText}); - writer.Write(_internal::XmlNode{_internal::XmlNodeType::EndTag}); - writer.Write(_internal::XmlNode{_internal::XmlNodeType::End}); - xmlBody = writer.GetDocument(); - } - Core::IO::MemoryBodyStream requestBody( - reinterpret_cast(xmlBody.data()), xmlBody.length()); - auto request = Core::Http::Request(Core::Http::HttpMethod::Put, url, &requestBody); - request.SetHeader("Content-Type", "application/xml; charset=UTF-8"); - request.SetHeader("Content-Length", std::to_string(requestBody.Length())); - if (!options.PopReceipt.empty()) - { - request.GetUrl().AppendQueryParameter( - "popreceipt", _internal::UrlEncodeQueryParameter(options.PopReceipt)); - } - request.GetUrl().AppendQueryParameter( - "visibilitytimeout", std::to_string(options.Visibilitytimeout)); - request.SetHeader("x-ms-version", "2026-04-06"); - auto pRawResponse = pipeline.Send(request, context); - auto httpStatusCode = pRawResponse->GetStatusCode(); - if (httpStatusCode != Core::Http::HttpStatusCode::NoContent) - { - throw StorageException::CreateFromResponse(std::move(pRawResponse)); - } - Models::UpdateMessageResult response; - response.PopReceipt = pRawResponse->GetHeaders().at("x-ms-popreceipt"); - response.NextVisibleOn = DateTime::Parse( - pRawResponse->GetHeaders().at("x-ms-time-next-visible"), - Azure::DateTime::DateFormat::Rfc1123); - return Response(std::move(response), std::move(pRawResponse)); - } - Response QueueClient::DeleteMessage( - Core::Http::_internal::HttpPipeline& pipeline, - const Core::Url& url, - const DeleteQueueMessageOptions& options, - const Core::Context& context) - { - auto request = Core::Http::Request(Core::Http::HttpMethod::Delete, url); - if (!options.PopReceipt.empty()) - { - request.GetUrl().AppendQueryParameter( - "popreceipt", _internal::UrlEncodeQueryParameter(options.PopReceipt)); - } - request.SetHeader("x-ms-version", "2026-04-06"); - auto pRawResponse = pipeline.Send(request, context); - auto httpStatusCode = pRawResponse->GetStatusCode(); - if (httpStatusCode != Core::Http::HttpStatusCode::NoContent) - { - throw StorageException::CreateFromResponse(std::move(pRawResponse)); - } - Models::DeleteMessageResult response; - return Response(std::move(response), std::move(pRawResponse)); - } - Response QueueClient::UpdateMessageVisibility( - Core::Http::_internal::HttpPipeline& pipeline, - const Core::Url& url, - const UpdateQueueMessageVisibilityOptions& options, - const Core::Context& context) - { - auto request = Core::Http::Request(Core::Http::HttpMethod::Put, url); - if (!options.PopReceipt.empty()) - { - request.GetUrl().AppendQueryParameter( - "popreceipt", _internal::UrlEncodeQueryParameter(options.PopReceipt)); - } - request.GetUrl().AppendQueryParameter( - "visibilitytimeout", std::to_string(options.Visibilitytimeout)); - request.SetHeader("x-ms-version", "2026-04-06"); - auto pRawResponse = pipeline.Send(request, context); - auto httpStatusCode = pRawResponse->GetStatusCode(); - if (httpStatusCode != Core::Http::HttpStatusCode::NoContent) - { - throw StorageException::CreateFromResponse(std::move(pRawResponse)); - } - Models::UpdateMessageResult response; - response.PopReceipt = pRawResponse->GetHeaders().at("x-ms-popreceipt"); - response.NextVisibleOn = DateTime::Parse( - pRawResponse->GetHeaders().at("x-ms-time-next-visible"), - Azure::DateTime::DateFormat::Rfc1123); - return Response(std::move(response), std::move(pRawResponse)); - } - } // namespace _detail -}}} // namespace Azure::Storage::Queues \ No newline at end of file diff --git a/sdk/storage/azure-storage-queues/swagger/README.md b/sdk/storage/azure-storage-queues/swagger/README.md index c1e5cc7424..05a2b6e821 100644 --- a/sdk/storage/azure-storage-queues/swagger/README.md +++ b/sdk/storage/azure-storage-queues/swagger/README.md @@ -1,4 +1,7 @@ -# Azure Storage C++ Protocol Layer +# Legacy Azure Storage C++ Protocol Layer + +This configuration is retained as historical migration reference. Queue protocol +code is now generated from TypeSpec; see [the generation instructions](../typespec/README.md). > see https://aka.ms/autorest diff --git a/sdk/storage/azure-storage-queues/test/ut/CMakeLists.txt b/sdk/storage/azure-storage-queues/test/ut/CMakeLists.txt index f01d02ef51..d3fd1227a9 100644 --- a/sdk/storage/azure-storage-queues/test/ut/CMakeLists.txt +++ b/sdk/storage/azure-storage-queues/test/ut/CMakeLists.txt @@ -20,6 +20,7 @@ add_executable ( queue_client_messages_test.cpp queue_client_test.cpp queue_client_test.hpp + queue_protocol_test.cpp queue_sas_test.cpp queue_service_client_test.cpp queue_service_client_test.hpp diff --git a/sdk/storage/azure-storage-queues/test/ut/queue_protocol_test.cpp b/sdk/storage/azure-storage-queues/test/ut/queue_protocol_test.cpp new file mode 100644 index 0000000000..c41bf5b535 --- /dev/null +++ b/sdk/storage/azure-storage-queues/test/ut/queue_protocol_test.cpp @@ -0,0 +1,652 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "../../src/private/queue_protocol.hpp" + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +namespace Azure { namespace Storage { namespace Test { namespace { + namespace Http = Core::Http; + using namespace Queues; + + Core::Context::Key const ContextKey; + char const* const Date = "Sun, 18 Oct 2026 10:20:30 GMT"; + char const* const Endpoint + = "https://account.queue.example/queue?sv=2026-04-06&sig=not-a-secret%2Bsample%3D"; + + struct Reply + { + Http::HttpStatusCode Status; + std::string Body; + Core::CaseInsensitiveMap Headers; + }; + + struct RecordedRequest + { + Core::Url Url; + Core::CaseInsensitiveMap Headers; + std::string Body; + std::string ContextValue; + bool HasReplicaStatus; + }; + + class ProtocolTransport final : public Http::HttpTransport { + public: + std::deque Replies; + std::vector Requests; + + std::unique_ptr Send(Http::Request& request, Core::Context const& context) + override + { + context.ThrowIfCancelled(); + if (Replies.empty()) + { + throw std::runtime_error("Unexpected request in Queue protocol test."); + } + std::string contextValue; + context.TryGetValue(ContextKey, contextValue); + std::shared_ptr replica; + auto const hasReplica + = context.TryGetValue(_internal::SecondaryHostReplicaStatusKey, replica); + std::string body; + if (auto stream = request.GetBodyStream()) + { + auto bytes = stream->ReadToEnd(context); + body.assign(bytes.begin(), bytes.end()); + } + Requests.push_back( + {request.GetUrl(), + request.GetHeaders(), + std::move(body), + std::move(contextValue), + hasReplica}); + auto reply = std::move(Replies.front()); + Replies.pop_front(); + auto response = std::make_unique(1, 1, reply.Status, "Test response"); + response->SetHeader("date", Date); + response->SetHeader("x-ms-version", "2026-04-06"); + response->SetHeader("x-ms-request-id", "test-request"); + if (!reply.Body.empty()) + { + response->SetHeader("content-type", "application/xml"); + } + for (auto const& header : reply.Headers) + { + response->SetHeader(header.first, header.second); + } + response->SetBody(std::vector(reply.Body.begin(), reply.Body.end())); + response->SetBodyStream(std::make_unique(response->GetBody())); + return response; + } + }; + + class CountingPolicy final : public Http::Policies::HttpPolicy { + private: + std::shared_ptr m_count; + + public: + explicit CountingPolicy(std::shared_ptr count) : m_count(std::move(count)) {} + + std::unique_ptr Clone() const override + { + return std::make_unique(m_count); + } + + std::unique_ptr Send( + Http::Request& request, + Http::Policies::NextHttpPolicy nextPolicy, + Core::Context const& context) const override + { + ++*m_count; + request.SetHeader("x-test-policy", "once"); + return nextPolicy.Send(request, context); + } + }; + + class QueueProtocolTest : public ::testing::Test { + protected: + std::shared_ptr Transport = std::make_shared(); + + QueueClientOptions Options() + { + QueueClientOptions options; + options.Transport.Transport = Transport; + options.Retry.MaxRetries = 0; + return options; + } + }; + + TEST_F(QueueProtocolTest, MetadataVersionSasAndIdempotency) + { + auto options = Options(); + options.ApiVersion = ServiceVersion::V2019_12_12; + auto count = std::make_shared(0); + options.PerOperationPolicies.emplace_back(std::make_unique(count)); + QueueClient client(Endpoint, options); + Transport->Replies + = {{Http::HttpStatusCode::Created, "", {}}, + {Http::HttpStatusCode::NoContent, "", {}}, + {Http::HttpStatusCode::Conflict, + "QueueAlreadyExistsExists", + {}}, + {Http::HttpStatusCode::NoContent, "", {}}, + {Http::HttpStatusCode::NotFound, + "QueueNotFoundMissing", + {}}, + {Http::HttpStatusCode::NoContent, "", {}}, + {Http::HttpStatusCode::BadRequest, + "InvalidMetadataInvalid", + {}}}; + + CreateQueueOptions create; + create.Metadata = {{"Name", "value"}, {"Empty", ""}}; + auto context = Core::Context().WithValue(ContextKey, std::string("forwarded")); + EXPECT_TRUE(client.Create(create, context).Value.Created); + EXPECT_FALSE(client.Create().Value.Created); + EXPECT_FALSE(client.Create().Value.Created); + EXPECT_TRUE(client.Delete().Value.Deleted); + EXPECT_FALSE(client.Delete().Value.Deleted); + client.SetMetadata({{"Other", "value"}}); + try + { + client.SetMetadata({}); + FAIL() << "Expected StorageException"; + } + catch (StorageException const& error) + { + EXPECT_EQ(error.ErrorCode, "InvalidMetadata"); + EXPECT_EQ(error.StatusCode, Http::HttpStatusCode::BadRequest); + ASSERT_NE(error.RawResponse, nullptr); + EXPECT_EQ(error.RawResponse->GetHeaders().at("x-ms-request-id"), "test-request"); + } + ASSERT_EQ(Transport->Requests.size(), 7U); + EXPECT_EQ(*count, 7); + EXPECT_EQ(Transport->Requests[0].ContextValue, "forwarded"); + EXPECT_EQ(Transport->Requests[0].Headers.at("x-ms-meta-name"), "value"); + EXPECT_EQ(Transport->Requests[0].Headers.at("x-ms-meta-empty"), ""); + EXPECT_EQ(Transport->Requests[5].Headers.at("x-ms-meta-other"), "value"); + EXPECT_EQ(Transport->Requests[5].Url.GetQueryParameters().at("comp"), "metadata"); + for (auto const& request : Transport->Requests) + { + EXPECT_EQ(request.Headers.at("x-ms-version"), "2019-12-12"); + EXPECT_EQ(request.Headers.at("x-test-policy"), "once"); + EXPECT_EQ(request.Headers.count("Accept"), 0U); + EXPECT_EQ(request.Url.GetQueryParameters().at("sig"), "not-a-secret%2Bsample%3D"); + } + } + + TEST_F(QueueProtocolTest, ApproximateMessageCountsAndMetadata) + { + QueueClient client(Endpoint, Options()); + Transport->Replies + = {{Http::HttpStatusCode::Ok, + "", + {{"x-ms-approximate-messages-count", "4294967296"}, {"x-ms-meta-name", "value"}}}, + {Http::HttpStatusCode::Ok, "", {{"x-ms-approximate-messages-count", "0"}}}, + {Http::HttpStatusCode::Ok, "", {}}}; + auto response = client.GetProperties(); + EXPECT_EQ(response.Value.ApproximateMessageCount, -1); + EXPECT_EQ(response.Value.ApproximateMessageCountLong, INT64_C(4294967296)); + EXPECT_EQ(response.Value.Metadata.at("name"), "value"); + EXPECT_EQ(client.GetProperties().Value.ApproximateMessageCount, 0); + EXPECT_THROW(client.GetProperties(), std::out_of_range); + } + + TEST_F(QueueProtocolTest, UserHeaderPoliciesTakePrecedence) + { + class HeaderPolicy final : public Http::Policies::HttpPolicy { + public: + std::unique_ptr Clone() const override + { + return std::make_unique(); + } + + std::unique_ptr Send( + Http::Request& request, + Http::Policies::NextHttpPolicy nextPolicy, + Core::Context const& context) const override + { + request.SetHeader("Accept", "application/custom"); + request.SetHeader("Content-Type", "application/custom"); + return nextPolicy.Send(request, context); + } + }; + auto options = Options(); + options.PerOperationPolicies.emplace_back(std::make_unique()); + QueueClient client(Endpoint, options); + QueueServiceClient service("https://account.queue.example", options); + Transport->Replies + = {{Http::HttpStatusCode::Created, "", {}}, + {Http::HttpStatusCode::Created, "", {}}}; + client.EnqueueMessage("message"); + service.GetQueueClient("queue").EnqueueMessage("message"); + ASSERT_EQ(Transport->Requests.size(), 2U); + for (auto const& request : Transport->Requests) + { + EXPECT_EQ(request.Headers.at("Accept"), "application/custom"); + EXPECT_EQ(request.Headers.at("Content-Type"), "application/custom"); + } + } + + TEST_F(QueueProtocolTest, StorageErrorsAcrossOperations) + { + QueueClient client(Endpoint, Options()); + QueueServiceClient service("https://account.queue.example", Options()); + auto const expiresOn = DateTime::Parse("2026-10-19T10:20:30Z", DateTime::DateFormat::Rfc3339); + std::vector>> const operations{ + {"Create", [&] { client.Create(); }}, + {"Delete", [&] { client.Delete(); }}, + {"GetProperties", [&] { client.GetProperties(); }}, + {"SetMetadata", [&] { client.SetMetadata({}); }}, + {"GetAccessPolicy", [&] { client.GetAccessPolicy(); }}, + {"SetAccessPolicy", [&] { client.SetAccessPolicy({}); }}, + {"EnqueueMessage", [&] { client.EnqueueMessage("message"); }}, + {"ReceiveMessages", [&] { client.ReceiveMessages(); }}, + {"PeekMessages", [&] { client.PeekMessages(); }}, + {"UpdateMessage", [&] { client.UpdateMessage("id", "receipt", std::chrono::seconds(60)); }}, + {"DeleteMessage", [&] { client.DeleteMessage("id", "receipt"); }}, + {"ClearMessages", [&] { client.ClearMessages(); }}, + {"ListQueues", [&] { service.ListQueues(); }}, + {"SetServiceProperties", [&] { service.SetProperties({}); }}, + {"GetServiceProperties", [&] { service.GetProperties(); }}, + {"GetStatistics", [&] { service.GetStatistics(); }}, + {"GetUserDelegationKey", [&] { service.GetUserDelegationKey(expiresOn); }}, + {"CreateQueue", [&] { service.CreateQueue("queue"); }}, + {"DeleteQueue", [&] { service.DeleteQueue("queue"); }}}; + for (auto const& operation : operations) + { + SCOPED_TRACE(operation.first); + Transport->Replies.push_back( + {Http::HttpStatusCode::BadRequest, + "InvalidMetadataInvalid metadata" + "extra detail", + {{"x-ms-client-request-id", "client-request"}}}); + try + { + operation.second(); + FAIL() << "Expected StorageException"; + } + catch (StorageException const& error) + { + EXPECT_EQ(error.ErrorCode, "InvalidMetadata"); + EXPECT_EQ(error.Message, "Invalid metadata"); + EXPECT_EQ(error.StatusCode, Http::HttpStatusCode::BadRequest); + EXPECT_EQ(error.RequestId, "test-request"); + EXPECT_EQ(error.ClientRequestId, "client-request"); + EXPECT_EQ(error.AdditionalInformation.at("Detail"), "extra detail"); + ASSERT_NE(error.RawResponse, nullptr); + EXPECT_EQ(error.RawResponse->GetStatusCode(), Http::HttpStatusCode::BadRequest); + } + } + EXPECT_EQ(Transport->Requests.size(), operations.size()); + EXPECT_TRUE(Transport->Replies.empty()); + } + + TEST_F(QueueProtocolTest, JsonTextAndEmptyErrors) + { + QueueClient client(Endpoint, Options()); + Transport->Replies + = {{Http::HttpStatusCode::BadRequest, + R"({"error":{"code":"InvalidMetadata","message":"JSON error"}})", + {{"content-type", "application/json"}}}, + {Http::HttpStatusCode::BadRequest, + "Text error", + {{"content-type", "text/plain"}, {"x-ms-error-code", "InvalidMetadata"}}}, + {Http::HttpStatusCode::BadRequest, "", {{"x-ms-error-code", "InvalidMetadata"}}}}; + for (auto const& message : {"JSON error", "Text error", ""}) + { + try + { + client.SetMetadata({}); + FAIL() << "Expected StorageException"; + } + catch (StorageException const& error) + { + EXPECT_EQ(error.ErrorCode, "InvalidMetadata"); + EXPECT_EQ(error.Message, message); + ASSERT_NE(error.RawResponse, nullptr); + EXPECT_EQ(error.RawResponse->GetStatusCode(), Http::HttpStatusCode::BadRequest); + } + } + EXPECT_TRUE(Transport->Replies.empty()); + } + + TEST_F(QueueProtocolTest, ContinuationErrors) + { + QueueServiceClient client("https://account.queue.example", Options()); + Transport->Replies + = {{Http::HttpStatusCode::Ok, + "" + "next", + {}}, + {Http::HttpStatusCode::BadRequest, + "InvalidMarkerInvalid marker", + {}}}; + auto page = client.ListQueues(); + try + { + page.MoveToNextPage(); + FAIL() << "Expected StorageException"; + } + catch (StorageException const& error) + { + EXPECT_EQ(error.ErrorCode, "InvalidMarker"); + EXPECT_EQ(error.Message, "Invalid marker"); + ASSERT_NE(error.RawResponse, nullptr); + } + ASSERT_EQ(Transport->Requests.size(), 2U); + EXPECT_EQ(Transport->Requests[1].Url.GetQueryParameters().at("marker"), "next"); + } + + TEST(QueueProtocolAdapterTest, PreservesExistingExceptions) + { + try + { + Queues::_detail::InvokeStorageProtocol([] { + StorageException error("existing Storage exception"); + error.ErrorCode = "Existing"; + error.AdditionalInformation["Detail"] = "retained"; + error.RawResponse + = std::make_unique(1, 1, Http::HttpStatusCode::Conflict, "Conflict"); + throw error; + }); + FAIL() << "Expected StorageException"; + } + catch (StorageException const& error) + { + EXPECT_STREQ(error.what(), "existing Storage exception"); + EXPECT_EQ(error.ErrorCode, "Existing"); + EXPECT_EQ(error.AdditionalInformation.at("Detail"), "retained"); + ASSERT_NE(error.RawResponse, nullptr); + } + try + { + Queues::_detail::InvokeStorageProtocol( + [] { throw Core::RequestFailedException("response parsing failed"); }); + FAIL() << "Expected Core::RequestFailedException"; + } + catch (StorageException const&) + { + FAIL() << "An exception without an HTTP response must not be translated"; + } + catch (Core::RequestFailedException const& error) + { + EXPECT_STREQ(error.what(), "response parsing failed"); + EXPECT_EQ(error.RawResponse, nullptr); + } + EXPECT_THROW( + Queues::_detail::InvokeStorageProtocol([] { throw std::logic_error("unrelated"); }), + std::logic_error); + auto value = Queues::_detail::InvokeStorageProtocol([] { return std::make_unique(42); }); + EXPECT_EQ(*value, 42); + } + + TEST_F(QueueProtocolTest, CancellationAndTransportFailures) + { + QueueClient client(Endpoint, Options()); + auto const cancelled = Core::Context::ApplicationContext.WithDeadline( + std::chrono::system_clock::now() - std::chrono::seconds(1)); + EXPECT_THROW(client.GetProperties({}, cancelled), Core::OperationCancelledException); + EXPECT_TRUE(Transport->Requests.empty()); + EXPECT_THROW(client.GetProperties(), std::runtime_error); + EXPECT_TRUE(Transport->Requests.empty()); + } + + TEST_F(QueueProtocolTest, MessageModelsAndVisibilityOnlyUpdates) + { + QueueClient client(Endpoint, Options()); + std::string const message + = "idSun, 18 Oct 2026 10:20:30 GMT" + "Sun, 25 Oct 2026 10:20:30 GMT" + "4294967296a&b"; + std::string const received = "" + message + + "receipt" + "Sun, 18 Oct 2026 10:21:30 GMT" + ""; + Core::CaseInsensitiveMap const updatedHeaders{ + {"x-ms-popreceipt", "updated"}, + {"x-ms-time-next-visible", "Sun, 18 Oct 2026 10:21:30 GMT"}}; + Transport->Replies + = {{Http::HttpStatusCode::Ok, received, {}}, + {Http::HttpStatusCode::Ok, + "" + message + "", + {}}, + {Http::HttpStatusCode::Created, received, {}}, + {Http::HttpStatusCode::NoContent, "", updatedHeaders}, + {Http::HttpStatusCode::NoContent, "", updatedHeaders}, + {Http::HttpStatusCode::NoContent, "", updatedHeaders}, + {Http::HttpStatusCode::NoContent, "", {}}, + {Http::HttpStatusCode::NoContent, "", {}}}; + ReceiveMessagesOptions receive; + receive.MaxMessages = INT64_C(2147483648); + auto messages = client.ReceiveMessages(receive); + ASSERT_EQ(messages.Value.Messages.size(), 1U); + EXPECT_EQ(messages.Value.Messages[0].DequeueCount, INT64_C(4294967296)); + EXPECT_EQ(messages.Value.Messages[0].MessageText, "a&b"); + EXPECT_EQ(messages.Value.Messages[0].PopReceipt, "receipt"); + PeekMessagesOptions peek; + peek.MaxMessages = INT64_C(2147483648); + auto peeked = client.PeekMessages(peek); + ASSERT_EQ(peeked.Value.Messages.size(), 1U); + EXPECT_EQ(peeked.Value.Messages[0].InsertedOn, messages.Value.Messages[0].InsertedOn); + EXPECT_EQ(peeked.Value.Messages[0].DequeueCount, INT64_C(4294967296)); + auto sent = client.EnqueueMessage("a&b"); + EXPECT_EQ(sent.Value.MessageId, "id"); + auto updated = client.UpdateMessage("id", "receipt", std::chrono::seconds(60)); + EXPECT_EQ(updated.Value.PopReceipt, "updated"); + UpdateMessageOptions update; + update.MessageText = ""; + client.UpdateMessage("id", "receipt", std::chrono::seconds(60), update); + update.MessageText = "a&b"; + client.UpdateMessage("id", "receipt", std::chrono::seconds(60), update); + client.DeleteMessage("id", "receipt"); + client.ClearMessages(); + ASSERT_EQ(Transport->Requests.size(), 8U); + EXPECT_EQ(Transport->Requests[0].Url.GetQueryParameters().at("numofmessages"), "2147483648"); + EXPECT_EQ(Transport->Requests[1].Url.GetQueryParameters().at("numofmessages"), "2147483648"); + EXPECT_EQ(Transport->Requests[1].Url.GetQueryParameters().at("peekonly"), "true"); + EXPECT_EQ(Transport->Requests[3].Body, ""); + EXPECT_EQ(Transport->Requests[3].Headers.count("Content-Type"), 0U); + auto const length = Transport->Requests[3].Headers.find("content-length"); + if (length != Transport->Requests[3].Headers.end()) + { + EXPECT_EQ(length->second, "0"); + } + EXPECT_NE(Transport->Requests[4].Body.find("MessageText"), std::string::npos); + EXPECT_NE(Transport->Requests[5].Body.find("a&b"), std::string::npos); + EXPECT_EQ(Transport->Requests[2].Headers.at("Content-Type"), "application/xml; charset=UTF-8"); + EXPECT_EQ(Transport->Requests[6].Url.GetQueryParameters().at("popreceipt"), "receipt"); + for (auto const& request : Transport->Requests) + { + EXPECT_EQ(request.Url.GetQueryParameters().at("sig"), "not-a-secret%2Bsample%3D"); + } + } + + TEST_F(QueueProtocolTest, AccessPolicyDatesAndEmptyLists) + { + QueueClient client(Endpoint, Options()); + Transport->Replies + = {{Http::HttpStatusCode::NoContent, "", {}}, + {Http::HttpStatusCode::Ok, + "id" + "2026-10-18T10:20:30Z2026-10-19T10:20:30Z" + "ra", + {}}, + {Http::HttpStatusCode::NoContent, "", {}}}; + Models::SignedIdentifier identifier; + identifier.Id = "id"; + identifier.StartsOn + = DateTime::Parse("2026-10-18T10:20:30.1234567Z", DateTime::DateFormat::Rfc3339); + identifier.ExpiresOn + = DateTime::Parse("2026-10-19T10:20:30.1234567Z", DateTime::DateFormat::Rfc3339); + identifier.Permissions = "ra"; + Models::QueueAccessPolicy policy; + policy.SignedIdentifiers.emplace_back(std::move(identifier)); + client.SetAccessPolicy(policy); + auto response = client.GetAccessPolicy(); + ASSERT_EQ(response.Value.SignedIdentifiers.size(), 1U); + EXPECT_EQ(response.Value.SignedIdentifiers[0].Permissions, "ra"); + EXPECT_EQ( + response.Value.SignedIdentifiers[0].StartsOn.Value(), + DateTime::Parse("2026-10-18T10:20:30Z", DateTime::DateFormat::Rfc3339)); + client.SetAccessPolicy({}); + ASSERT_EQ(Transport->Requests.size(), 3U); + EXPECT_NE( + Transport->Requests[0].Body.find("2026-10-18T10:20:30.1234567Z"), + std::string::npos); + EXPECT_EQ( + Transport->Requests[0].Body.find(""), + std::string::npos); + EXPECT_NE(Transport->Requests[2].Body.find("SignedIdentifiers"), std::string::npos); + } + + TEST_F(QueueProtocolTest, AccessPolicyPreservesFractionalSecondZeros) + { + QueueClient client(Endpoint, Options()); + for (auto const* fraction : {"0000000", "1230000", "1234567"}) + { + SCOPED_TRACE(fraction); + auto const timestamp = std::string("2026-10-18T10:20:30.") + fraction + "Z"; + Models::SignedIdentifier identifier; + identifier.Id = "id"; + identifier.StartsOn = DateTime::Parse(timestamp, DateTime::DateFormat::Rfc3339); + identifier.ExpiresOn = identifier.StartsOn; + identifier.Permissions = "ra"; + Models::QueueAccessPolicy policy; + policy.SignedIdentifiers.emplace_back(std::move(identifier)); + Transport->Replies.push_back({Http::HttpStatusCode::NoContent, "", {}}); + client.SetAccessPolicy(policy); + auto const& body = Transport->Requests.back().Body; + EXPECT_NE(body.find("" + timestamp + ""), std::string::npos); + EXPECT_NE(body.find("" + timestamp + ""), std::string::npos); + } + } + + TEST_F(QueueProtocolTest, ServicePropertiesAndStatistics) + { + QueueServiceClient client("https://account.queue.example?sig=not-a-secret", Options()); + Transport->Replies + = {{Http::HttpStatusCode::Accepted, "", {}}, + {Http::HttpStatusCode::Ok, + "1.0true" + "falsetruetrue" + "31.0" + "falsefalse" + "", + {}}, + {Http::HttpStatusCode::Ok, + "live" + "Sun, 18 Oct 2026 10:20:30 GMT" + "", + {}}, + {Http::HttpStatusCode::Ok, + "unavailable" + "", + {}}}; + Models::QueueServiceProperties properties; + properties.Logging.Version = "1.0"; + properties.Logging.RetentionPolicy.IsEnabled = true; + properties.Logging.RetentionPolicy.Days = 3; + properties.HourMetrics.Version = "1.0"; + properties.MinuteMetrics.Version = "1.0"; + client.SetProperties(properties); + auto response = client.GetProperties(); + EXPECT_EQ(response.Value.Logging.Version, "1.0"); + EXPECT_TRUE(response.Value.Logging.Delete); + EXPECT_TRUE(response.Value.Logging.Write); + EXPECT_TRUE(response.Value.Logging.RetentionPolicy.IsEnabled); + EXPECT_EQ(response.Value.Logging.RetentionPolicy.Days.Value(), 3); + EXPECT_FALSE(response.Value.HourMetrics.IsEnabled); + EXPECT_FALSE(response.Value.HourMetrics.IncludeApis.HasValue()); + EXPECT_TRUE(response.Value.Cors.empty()); + auto stats = client.GetStatistics(); + EXPECT_EQ(stats.Value.GeoReplication.Status, Models::GeoReplicationStatus::Live); + ASSERT_TRUE(stats.Value.GeoReplication.LastSyncedOn.HasValue()); + EXPECT_EQ( + stats.Value.GeoReplication.LastSyncedOn.Value(), + DateTime::Parse("2026-10-18T10:20:30Z", DateTime::DateFormat::Rfc3339)); + EXPECT_FALSE(client.GetStatistics().Value.GeoReplication.LastSyncedOn.HasValue()); + ASSERT_EQ(Transport->Requests.size(), 4U); + EXPECT_NE(Transport->Requests[0].Body.find(""), std::string::npos); + EXPECT_EQ(Transport->Requests[0].Headers.at("Content-Type"), "application/xml; charset=UTF-8"); + EXPECT_EQ(Transport->Requests[1].Headers.count("Accept"), 0U); + EXPECT_EQ(Transport->Requests[0].Body.find(""), std::string::npos); + EXPECT_EQ(Transport->Requests[0].Url.GetQueryParameters().at("restype"), "service"); + EXPECT_TRUE(Transport->Requests[1].HasReplicaStatus); + } + + TEST_F(QueueProtocolTest, PagingAndDelegationKey) + { + QueueServiceClient client("https://account.queue.example?sig=not-a-secret", Options()); + Transport->Replies + = {{Http::HttpStatusCode::Ok, + "pre" + "firstvalue" + "next marker", + {}}, + {Http::HttpStatusCode::Ok, + "pre" + "", + {}}, + {Http::HttpStatusCode::Ok, + "objecttenant" + "2026-10-18T10:20:30Z" + "2026-10-19T10:20:30Zq" + "2026-04-06sample-key" + "", + {}}}; + ListQueuesOptions list; + list.Prefix = "pre"; + list.Include = Models::ListQueuesIncludeFlags::Metadata; + auto page = client.ListQueues(list); + ASSERT_EQ(page.Queues.size(), 1U); + EXPECT_EQ(page.ServiceEndpoint, "https://account.queue.example"); + EXPECT_EQ(page.Queues[0].Name, "first"); + EXPECT_EQ(page.Queues[0].Metadata.at("name"), "value"); + EXPECT_EQ(page.Queues[0].Metadata.at("empty"), ""); + EXPECT_EQ(page.NextPageToken.Value(), "next marker"); + page.MoveToNextPage(); + EXPECT_TRUE(page.Queues.empty()); + EXPECT_FALSE(page.NextPageToken.HasValue()); + GetUserDelegationKeyOptions keyOptions; + keyOptions.StartsOn + = DateTime::Parse("2026-10-18T10:20:30.1234567Z", DateTime::DateFormat::Rfc3339); + keyOptions.DelegatedUserTid = "delegated"; + auto key = client.GetUserDelegationKey( + DateTime::Parse("2026-10-19T10:20:30.1234567Z", DateTime::DateFormat::Rfc3339), keyOptions); + EXPECT_EQ(key.Value.SignedObjectId, "object"); + EXPECT_EQ(key.Value.SignedTenantId, "tenant"); + EXPECT_EQ( + key.Value.SignedStartsOn, + DateTime::Parse("2026-10-18T10:20:30Z", DateTime::DateFormat::Rfc3339)); + EXPECT_EQ( + key.Value.SignedExpiresOn, + DateTime::Parse("2026-10-19T10:20:30Z", DateTime::DateFormat::Rfc3339)); + EXPECT_EQ(key.Value.Value, "sample-key"); + ASSERT_EQ(Transport->Requests.size(), 3U); + EXPECT_TRUE(Transport->Requests[0].HasReplicaStatus); + EXPECT_TRUE(Transport->Requests[1].HasReplicaStatus); + EXPECT_EQ(Transport->Requests[0].Url.GetQueryParameters().at("include"), "metadata"); + EXPECT_EQ(Transport->Requests[1].Url.GetQueryParameters().at("marker"), "next%20marker"); + EXPECT_NE( + Transport->Requests[2].Body.find("2026-10-18T10:20:30Z"), std::string::npos); + EXPECT_NE( + Transport->Requests[2].Body.find("2026-10-19T10:20:30Z"), + std::string::npos); + EXPECT_NE( + Transport->Requests[2].Body.find("delegated"), + std::string::npos); + } +}}}} // namespace Azure::Storage::Test:: diff --git a/sdk/storage/azure-storage-queues/tsp-location.yaml b/sdk/storage/azure-storage-queues/tsp-location.yaml new file mode 100644 index 0000000000..eda1750d04 --- /dev/null +++ b/sdk/storage/azure-storage-queues/tsp-location.yaml @@ -0,0 +1,3 @@ +directory: specification/storage/data-plane/QueueStorage +commit: ba3eef39e5be55cd5ed1a2e5c6c9bb1a17c36b9a +repo: Azure/azure-rest-api-specs diff --git a/sdk/storage/azure-storage-queues/tspconfig.yaml b/sdk/storage/azure-storage-queues/tspconfig.yaml new file mode 100644 index 0000000000..08914785cb --- /dev/null +++ b/sdk/storage/azure-storage-queues/tspconfig.yaml @@ -0,0 +1,11 @@ +emit: + - "@azure-tools/typespec-cpp" +options: + "@azure-tools/typespec-cpp": + flavor: azure + namespace: Azure::Storage::Queues::_detail + headersOutputPath: azure/storage/queues + noPublicHeaders: true + pipelineMode: reuse + includeHeadersInResponse: true + api-version: "2026-04-06" diff --git a/sdk/storage/azure-storage-queues/typespec/README.md b/sdk/storage/azure-storage-queues/typespec/README.md new file mode 100644 index 0000000000..63d673ef96 --- /dev/null +++ b/sdk/storage/azure-storage-queues/typespec/README.md @@ -0,0 +1,75 @@ +# Queue protocol generation + +Queue operations are generated directly from TypeSpec using +[`Azure/typespec-cpp`](https://github.com/Azure/typespec-cpp). +`tsp-location.yaml` pins the service specification and `tspconfig.yaml` selects +the service version and internal protocol generation. + +`includeHeadersInResponse: true` explicitly enables the typed response +headers consumed by the compatibility layer. + +All generated code is in `src/generated`, under +`Azure::Storage::Queues::_detail`. The public clients reuse their existing +Storage pipelines and convert generated models to the existing public models. +The handwritten layer translates Core HTTP exceptions into `StorageException` +using the existing Storage error parser; no emitter-specific error factory is +needed. +An internal pipeline policy also removes the emitter's default `Accept` header +and retains `application/xml; charset=UTF-8` for XML requests before user policies +run. Requests without an XML body omit `Content-Type`, including visibility-only +message updates. This preserves the previous Queue wire behavior and compatibility +with existing recordings. User policies can still override these headers. +The public `inc/azure/storage/queues/rest_client.hpp` is retained for source +compatibility, including existing include paths, fields, defaults, and enums. +Do not replace it with the generated model header or edit generated C++ files. + +## Regeneration + +The emitter needs the Queue migration changes: XML metadata and nullable values, +header collections and scalar headers, SAS-safe query parameters, pipeline reuse, +and optional request bodies. Use merged emitter revision +`810b64ca434127b81547fbbabec039fe2d9dc4f2` +([Azure/typespec-cpp#585](https://github.com/Azure/typespec-cpp/pull/585)), +including the default-off response-header option, intrinsic optional ETags, and +fixed-precision XML date-times, and optional-body Content-Type handling. +Older published emitters are not sufficient. +Generate from a clean snapshot of that revision, not from uncommitted emitter +experiments. + +Build `packages/codegen.cpp` first, then `packages/typespec-cpp`, using the +emitter repository's package manifests. Use its compiler and dependencies rather +than independently installing the latest TypeSpec packages. The migration was +generated with compiler 1.12.0 and client-generator-core 0.68.4. + +Provide a QueueStorage specification directory from the exact revision in +`tsp-location.yaml`. Its `client.tsp` and `routes.tsp` contain the C++-scoped +customizations maintained in +[Azure/azure-rest-api-specs#47555](https://github.com/Azure/azure-rest-api-specs/pull/47555). +No package-local TypeSpec overlay is needed. From this package directory, run: + +```powershell +.\typespec\generate.ps1 ` + -EmitterPath C:\path\to\typespec-cpp\packages\typespec-cpp ` + -SpecDirectory C:\path\to\azure-rest-api-specs\specification\storage\data-plane\QueueStorage ` + -ClangFormatPath C:\path\to\clang-format-11\clang-format.exe +``` + +Use clang-format 11.0.0. The script stages the pinned service's four TypeSpec +files and generates directly from their `client.tsp` in a temporary +directory. It formats the output before copying the five generated files into +the package. `-OutputDirectory` can redirect the output for comparison without +changing the package. + +After regeneration, build the package and run the offline `QueueProtocolTest.*` +tests, then the existing playback tests when test-proxy assets are available. +The offline tests cover public model conversions, metadata, service versions, +SAS URLs, XML, paging, error handling, and visibility-only updates. + +The service's C++ projection preserves nullable RFC1123 statistics dates and 64-bit +receive/peek count options, supplies the `StorageServiceStats` XML root, and +enables metadata header collections. ACL dates retain all seven fractional-second +digits. Delegation-key request dates are projected as strings and formatted by +the handwritten client without fractional seconds, as required by that endpoint. +The pinned service applies `@alternateType(int64, "cpp")` directly to the +receive/peek count declarations in `routes.tsp`. These decorators affect only +the C++ projection; no JavaScript helper is needed. diff --git a/sdk/storage/azure-storage-queues/typespec/generate.ps1 b/sdk/storage/azure-storage-queues/typespec/generate.ps1 new file mode 100644 index 0000000000..0b0362ab21 --- /dev/null +++ b/sdk/storage/azure-storage-queues/typespec/generate.ps1 @@ -0,0 +1,69 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +#Requires -Version 7.0 +[CmdletBinding()] +param( + [Parameter(Mandatory)] + [string] $EmitterPath, + + [Parameter(Mandatory)] + [string] $SpecDirectory, + + [string] $ClangFormatPath = "clang-format", + + [string] $OutputDirectory = (Split-Path $PSScriptRoot -Parent) +) + +$ErrorActionPreference = "Stop" +$emitter = (Resolve-Path -LiteralPath $EmitterPath).Path +$spec = (Resolve-Path -LiteralPath $SpecDirectory).Path +$compiler = Join-Path $emitter "node_modules\@typespec\compiler\cmd\tsp.js" +if (!(Test-Path -LiteralPath $compiler) -or !(Test-Path (Join-Path $emitter "dist\src\emitter.js"))) { + throw "Build packages\codegen.cpp and packages\typespec-cpp before generating." +} +$formatVersion = & $ClangFormatPath --version +if ($LASTEXITCODE -ne 0 -or $formatVersion -notmatch "clang-format version 11\.0\.0\b") { + throw "Generation requires clang-format 11.0.0." +} + +$work = Join-Path ([System.IO.Path]::GetTempPath()) ("queues-typespec-" + [guid]::NewGuid()) +$modules = Join-Path $work "node_modules" +New-Item -ItemType Directory -Path (Join-Path $work "service") | Out-Null +try { + foreach ($file in @("client.tsp", "main.tsp", "models.tsp", "routes.tsp")) { + Copy-Item -LiteralPath (Join-Path $spec $file) -Destination (Join-Path $work "service") + } + Copy-Item -LiteralPath (Join-Path (Split-Path $PSScriptRoot -Parent) "tspconfig.yaml") -Destination $work + Copy-Item -LiteralPath (Join-Path $PSScriptRoot "..\..\..\..\.clang-format") -Destination $work + $linkType = if ($IsWindows) { "Junction" } else { "SymbolicLink" } + New-Item -ItemType $linkType -Path $modules -Target (Join-Path $emitter "node_modules") | Out-Null + + & node $compiler compile (Join-Path $work "service\client.tsp") ` + --config (Join-Path $work "tspconfig.yaml") --emit $emitter ` + --output-dir (Join-Path $work "output") --pretty false + if ($LASTEXITCODE -ne 0) { + throw "Queue TypeSpec generation failed with exit code $LASTEXITCODE." + } + + $generated = Join-Path $work "output\src\generated" + $files = @("queues_client.cpp", "queues.cpp", "queues_client.hpp", + "queues_client_options.hpp", "queues_models.hpp") + foreach ($file in $files) { + & $ClangFormatPath -i --style=file (Join-Path $generated $file) + if ($LASTEXITCODE -ne 0) { + throw "Formatting $file failed with exit code $LASTEXITCODE." + } + } + $destination = Join-Path ([System.IO.Path]::GetFullPath($OutputDirectory)) "src\generated" + New-Item -ItemType Directory -Path $destination -Force | Out-Null + foreach ($file in $files) { + Copy-Item -LiteralPath (Join-Path $generated $file) -Destination $destination -Force + } +} +finally { + if (Test-Path -LiteralPath $modules) { + [System.IO.Directory]::Delete($modules) + } + Remove-Item -LiteralPath $work -Recurse -Force +} From 4a168775c2eceef64178613445420161a9a0e865 Mon Sep 17 00:00:00 2001 From: Jinming Hu Date: Sat, 24 Oct 2026 10:33:00 +1100 Subject: [PATCH 02/13] Keep Queue protocol generation tooling user-local Remove historical Swagger configuration and repository generation instructions/configuration. Drop the transparent internal migration changelog entry; retain generated runtime code, adapters and tests unchanged. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- sdk/storage/azure-storage-queues/CHANGELOG.md | 2 - .../azure-storage-queues/swagger/README.md | 463 ------------------ .../azure-storage-queues/tsp-location.yaml | 3 - .../azure-storage-queues/tspconfig.yaml | 11 - .../azure-storage-queues/typespec/README.md | 75 --- .../typespec/generate.ps1 | 69 --- 6 files changed, 623 deletions(-) delete mode 100644 sdk/storage/azure-storage-queues/swagger/README.md delete mode 100644 sdk/storage/azure-storage-queues/tsp-location.yaml delete mode 100644 sdk/storage/azure-storage-queues/tspconfig.yaml delete mode 100644 sdk/storage/azure-storage-queues/typespec/README.md delete mode 100644 sdk/storage/azure-storage-queues/typespec/generate.ps1 diff --git a/sdk/storage/azure-storage-queues/CHANGELOG.md b/sdk/storage/azure-storage-queues/CHANGELOG.md index 2fb162dd7a..cc8128ebc1 100644 --- a/sdk/storage/azure-storage-queues/CHANGELOG.md +++ b/sdk/storage/azure-storage-queues/CHANGELOG.md @@ -10,8 +10,6 @@ ### Other Changes -- Migrated the internal REST protocol layer to TypeSpec generation without changing public APIs. - ## 12.8.0 (2026-09-29) ### Other Changes diff --git a/sdk/storage/azure-storage-queues/swagger/README.md b/sdk/storage/azure-storage-queues/swagger/README.md deleted file mode 100644 index 05a2b6e821..0000000000 --- a/sdk/storage/azure-storage-queues/swagger/README.md +++ /dev/null @@ -1,463 +0,0 @@ -# Legacy Azure Storage C++ Protocol Layer - -This configuration is retained as historical migration reference. Queue protocol -code is now generated from TypeSpec; see [the generation instructions](../typespec/README.md). - -> see https://aka.ms/autorest - -## Configuration - -```yaml -package-name: azure-storage-queues -namespace: Azure::Storage::Queues -output-folder: generated -clear-output-folder: true -input-file: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/refs/heads/main/specification/storage/data-plane/Microsoft.QueueStorage/stable/2026-04-06/queue.json -``` - -## ModelFour Options - -```yaml -modelerfour: - naming: - property: pascal - parameter: pascal -``` - -## Customizations for Track 2 Generator - -See the [AutoRest samples](https://github.com/Azure/autorest/tree/master/Samples/3b-custom-transformations) -for more about how we're customizing things. - -### Fix Generator Warnings - -```yaml -directive: - - from: swagger-document - where: $.info - transform: > - delete $["x-ms-code-generation-settings"]; - - from: swagger-document - where: $.definitions - transform: > - delete $.DequeuedMessagesList.items.xml; - delete $.PeekedMessagesList.items.xml; - delete $.EnqueuedMessageList.items.xml; - delete $.SignedIdentifiers.items.xml; - delete $.StorageServiceProperties.properties.Cors.items.xml; -``` - -### Delete Unused Query Parameters and Headers - -```yaml -directive: - - from: swagger-document - where: $["x-ms-paths"].*.*.parameters - transform: > - $ = $.filter(p => !(p["$ref"] && (p["$ref"].endsWith("#/parameters/Timeout") || p["$ref"].endsWith("#/parameters/ClientRequestId")))); - - from: swagger-document - where: $["x-ms-paths"].*.*.responses.*.headers - transform: > - for (const h in $) { - if (["x-ms-client-request-id", "x-ms-request-id", "x-ms-version", "Date"].includes(h)) { - delete $[h]; - } - } -``` - -### API Version - -```yaml -directive: - - from: swagger-document - where: $.definitions - transform: > - $.ApiVersion = { - "type": "string", - "x-ms-export": true, - "x-namespace": "_detail", - "x-ms-enum": { - "name": "ApiVersion", - "modelAsString": false - }, - "enum": ["2026-04-06"], - "description": "The version used for the operations to Azure storage services." - }; - - from: swagger-document - where: $.parameters - transform: > - $.ApiVersionParameter.enum = ["2026-04-06"]; -``` - -### Rename Operations - -```yaml -directive: - - from: swagger-document - where: $["x-ms-paths"] - transform: > - $["/{queueName}/messages"].get.operationId = "Queue_ReceiveMessages"; - $["/{queueName}/messages"].delete.operationId = "Queue_ClearMessages"; - $["/{queueName}/messages"].post.operationId = "Queue_EnqueueMessage"; - $["/{queueName}/messages?peekonly=true"].get.operationId = "Queue_PeekMessages"; - $["/{queueName}/messages/{messageid}"].put.operationId = "Queue_UpdateMessage"; - $["/{queueName}/messages/{messageid}"].delete.operationId = "Queue_DeleteMessage"; -``` - -### Define names for return types - -```yaml -directive: - - from: swagger-document - where: $ - transform: > - const operationReturnTypeNames = new Map(Object.entries({ - "Queue_ClearMessages": "ClearMessagesResult", - "Queue_UpdateMessage": "UpdateMessageResult", - "Queue_DeleteMessage": "DeleteMessageResult", - "Service_SetProperties": "SetServicePropertiesResult", - })); - for (const url in $["x-ms-paths"]) { - for (const verb in $["x-ms-paths"][url]) { - const operation = $["x-ms-paths"][url][verb]; - if (!operationReturnTypeNames.has(operation.operationId)) { - continue; - } - const returnTypeName = operationReturnTypeNames.get(operation.operationId); - const status_codes = Object.keys(operation.responses).filter(s => s !== "default"); - const emptySchemaDefinition = { - "type": "object", - "x-ms-client-name": returnTypeName, - "x-ms-sealed": false, - "properties": { - "__placeHolder": {"type": "integer"} - } - }; - $.definitions[returnTypeName] = emptySchemaDefinition; - status_codes.map(i => { - operation.responses[i].schema = {"$ref": `#/definitions/${returnTypeName}`}; - }); - } - } - - from: swagger-document - where: $.definitions - transform: > - $.KeyInfo["x-namespace"] = "_detail"; -``` - -### Global Changes for Definitions, Types etc. - -```yaml -directive: - - from: swagger-document - where: $.parameters - transform: > - $.NumOfMessages.format = "int64"; - $.MaxResults["x-ms-client-name"] = "MaxResults"; -``` - -### GetServiceProperties - -```yaml -directive: - - from: swagger-document - where: $.definitions - transform: > - $.QueueServiceProperties = $.StorageServiceProperties; - delete $.StorageServiceProperties; - $.QueueServiceProperties.xml = { "name": "StorageServiceProperties" }; - $.RetentionPolicy.properties["Enabled"]["x-ms-client-name"] = "IsEnabled"; - $.AnalyticsLogging = $.Logging; - delete $.Logging; - $.AnalyticsLogging.xml = { "name": "Logging" }; - $.QueueServiceProperties.properties["Logging"]["$ref"] = "#/definitions/AnalyticsLogging"; - $.Metrics["type"] = "object"; - $.Metrics.properties["Enabled"]["x-ms-client-name"] = "IsEnabled"; - $.Metrics.properties["IncludeAPIs"]["x-ms-client-name"] = "IncludeApis"; - delete $.Metrics.required; - $.Metrics.properties["IncludeAPIs"]["x-nullable"] = true; - - from: swagger-document - where: $.parameters - transform: > - $.QueueServiceProperties = $.StorageServiceProperties; - $.QueueServiceProperties.name = "QueueServiceProperties"; - $.QueueServiceProperties.schema["$ref"] = "#/definitions/QueueServiceProperties"; - delete $.StorageServiceProperties; - - from: swagger-document - where: $["x-ms-paths"]["/?restype=service&comp=properties"] - transform: > - $.put.parameters[0]["$ref"] = "#/parameters/QueueServiceProperties"; - $.get.responses["200"].schema["$ref"] = "#/definitions/QueueServiceProperties"; -``` - -### GetServiceStatistics - -```yaml -directive: - - from: swagger-document - where: $.definitions - transform: > - $.ServiceStatistics = $.StorageServiceStats; - delete $.StorageServiceStats; - $.ServiceStatistics["xml"] = { "name": "StorageServiceStats" }; - $.GeoReplication.properties["LastSyncTime"]["x-ms-client-name"] = "LastSyncedOn"; - $.GeoReplication.required = ["Status"]; - $.GeoReplication.properties["Status"]["x-ms-enum"]["name"] = "GeoReplicationStatus"; - - from: swagger-document - where: $["x-ms-paths"]["/?restype=service&comp=stats"] - transform: > - $.get.responses["200"].schema["$ref"] = "#/definitions/ServiceStatistics"; -``` - -### GetUserDelegationKey - -```yaml -directive: - - from: swagger-document - where: $.definitions - transform: > - $.UserDelegationKey.properties["SignedOid"]["x-ms-client-name"] = "SignedObjectId"; - $.UserDelegationKey.properties["SignedTid"]["x-ms-client-name"] = "SignedTenantId"; - $.UserDelegationKey.properties["SignedStart"]["x-ms-client-name"] = "SignedStartsOn"; - $.UserDelegationKey.properties["SignedExpiry"]["x-ms-client-name"] = "SignedExpiresOn"; -``` - -### ListQueues - -```yaml -directive: - - from: swagger-document - where: $.parameters - transform: > - $.ListQueuesInclude.items["x-ms-enum"]["name"] = "ListQueuesIncludeFlags"; - - from: swagger-document - where: $.definitions - transform: > - $.Metadata = {"type": "object", "x-ms-format": "caseinsensitivemap", properties: {"__placeHolder" : {"type": "integer"}}}; - $.ListQueuesSegmentResponse["x-ms-client-name"] = "ListQueuesResult"; - $.ListQueuesSegmentResponse["x-namespace"] = "_detail"; - $.ListQueuesSegmentResponse.properties["QueueItems"]["x-ms-client-name"] = "Items"; - $.ListQueuesSegmentResponse.properties["QueueItems"]["x-ms-xml"] = {"name": "Queues"}; - $.ListQueuesSegmentResponse.properties["NextMarker"]["x-ms-client-name"] = "ContinuationToken"; - $.ListQueuesSegmentResponse.required = ["ServiceEndpoint", "Prefix"]; - delete $.ListQueuesSegmentResponse.properties["Marker"]; - delete $.ListQueuesSegmentResponse.properties["MaxResults"]; -``` - -### CreateQueue - -```yaml -directive: - - from: swagger-document - where: $.definitions - transform: > - $.CreateQueueResult = { - "type": "object", - "properties": { - "Created": {"type": "boolean", "x-ms-xml": {"name": ""}} - } - }; - - from: swagger-document - where: $["x-ms-paths"]["/{queueName}"].put.responses - transform: > - $["201"].schema = {"$ref": "#/definitions/CreateQueueResult"}; - $["204"].schema = {"$ref": "#/definitions/CreateQueueResult"}; -``` - -### DeleteQueue - -```yaml -directive: - - from: swagger-document - where: $["x-ms-paths"]["/{queueName}"].delete.responses["204"] - transform: > - $.schema = { - "type": "object", - "x-ms-client-name": "DeleteQueueResult", - "x-ms-sealed": false, - "properties": { - "Deleted": {"type": "boolean", "x-ms-client-default": true, "x-ms-xml": {"name": ""}} - } - }; -``` - - -### GetQueueProperties - -```yaml -directive: - - from: swagger-document - where: $["x-ms-paths"]["/{queueName}?comp=metadata"].get.responses["200"].headers - transform: > - $["x-ms-meta"]["x-ms-format"] = "caseinsensitivemap"; - $["x-ms-approximate-messages-count"]["x-ms-client-name"] = "ApproximateMessageCount"; - - from: swagger-document - where: $["x-ms-paths"]["/{queueName}?comp=metadata"].get.responses["200"] - transform: > - $.schema = { - "type": "object", - "x-ms-client-name": "QueueProperties", - "x-ms-sealed": false, - "x-namespace": "_detail", - "properties": {"__placeHolder": {"type": "integer"}} - }; -``` - -### GetQueueAccessPolicy - -```yaml -directive: - - from: swagger-document - where: $.definitions - transform: > - delete $.SignedIdentifier.properties["AccessPolicy"]; - $.SignedIdentifier.properties = {...$.SignedIdentifier.properties, ...$.AccessPolicy.properties}; - $.SignedIdentifier.properties["Start"]["x-ms-client-name"] = "StartsOn"; - $.SignedIdentifier.properties["Start"]["x-ms-xml"] = {"name": "AccessPolicy/Start"}; - $.SignedIdentifier.properties["Expiry"]["x-ms-client-name"] = "ExpiresOn"; - $.SignedIdentifier.properties["Expiry"]["x-ms-xml"] = {"name": "AccessPolicy/Expiry"}; - $.SignedIdentifier.properties["Permission"]["x-ms-client-name"] = "Permissions"; - $.SignedIdentifier.properties["Permission"]["x-ms-xml"] = {"name": "AccessPolicy/Permission"}; - $.SignedIdentifier.required = ["Id", "Permission"]; - delete $.AccessPolicy; - - from: swagger-document - where: $["x-ms-paths"]["/{queueName}?comp=acl"].get.responses["200"] - transform: > - $.schema = { - "type": "object", - "x-ms-client-name": "QueueAccessPolicy", - "xml": {"name": "."}, - "x-ms-sealed": false, - "properties": { - "SignedIdentifiers": {"$ref": "#/definitions/SignedIdentifiers"} - } - } -``` - -### ReceiveMessages - -```yaml -directive: - - from: swagger-document - where: $.definitions - transform: > - $.DequeuedMessageItem["x-ms-client-name"] = "QueueMessage"; - $.DequeuedMessageItem.properties["InsertionTime"]["x-ms-client-name"] = "InsertedOn"; - $.DequeuedMessageItem.properties["ExpirationTime"]["x-ms-client-name"] = "ExpiresOn"; - $.DequeuedMessageItem.properties["TimeNextVisible"]["x-ms-client-name"] = "NextVisibleOn"; - - from: swagger-document - where: $["x-ms-paths"]["/{queueName}/messages"].get.responses["200"] - transform: > - $.schema = { - "type": "object", - "x-ms-client-name": "ReceivedMessages", - "xml": {"name": "."}, - "x-ms-sealed": false, - "properties": { - "Messages": {"$ref": "#/definitions/DequeuedMessagesList", "x-ms-xml": {"name": "QueueMessagesList"}} - } - } -``` - -### UpdateMessage - -```yaml -directive: - - from: swagger-document - where: $.definitions - transform: > - $.QueueMessage["x-ms-client-name"] = "QueueMessageInternal"; - $.QueueMessage.xml = {"name": "QueueMessage"}; - $.QueueMessage["x-namespace"] = "_detail"; -``` - -### EnqueueMessage - -```yaml -directive: - - from: swagger-document - where: $.definitions - transform: > - $.EnqueuedMessage["x-ms-client-name"] = "EnqueueMessageResult"; - $.EnqueuedMessage.properties["InsertionTime"]["x-ms-client-name"] = "InsertedOn"; - $.EnqueuedMessage.properties["ExpirationTime"]["x-ms-client-name"] = "ExpiresOn"; - $.EnqueuedMessage.properties["TimeNextVisible"]["x-ms-client-name"] = "NextVisibleOn"; - $.EnqueuedMessage.xml.name = "QueueMessagesList/QueueMessage"; - - from: swagger-document - where: $["x-ms-paths"]["/{queueName}/messages"].post.responses["201"] - transform: > - $.schema["$ref"] = "#/definitions/EnqueuedMessage"; -``` - -### PeekMessages - -```yaml -directive: - - from: swagger-document - where: $.definitions - transform: > - $.PeekedMessageItem["x-ms-client-name"] = "PeekedQueueMessage"; - $.PeekedMessageItem.properties["InsertionTime"]["x-ms-client-name"] = "InsertedOn"; - $.PeekedMessageItem.properties["ExpirationTime"]["x-ms-client-name"] = "ExpiresOn"; - - from: swagger-document - where: $["x-ms-paths"]["/{queueName}/messages?peekonly=true"].get.responses["200"] - transform: > - $.schema = { - "type": "object", - "x-ms-client-name": "PeekedMessages", - "xml": {"name": "."}, - "x-ms-sealed": false, - "properties": { - "Messages": {"$ref": "#/definitions/PeekedMessagesList", "x-ms-xml": {"name": "QueueMessagesList"}} - } - } -``` - -### UpdateMessage - -```yaml -directive: - - from: swagger-document - where: $["x-ms-paths"]["/{queueName}/messages/{messageid}"].put - transform: > - $.responses["204"].headers["x-ms-time-next-visible"]["x-ms-client-name"] = "NextVisibleOn"; -``` - -### UpdateMessageVisibility - -```yaml -directive: - - from: swagger-document - where: $["x-ms-paths"] - transform: > - $["/{queueName}/messages/{messageid}?updatevisibilityonly=true"] = {}; - $["/{queueName}/messages/{messageid}?updatevisibilityonly=true"].put = JSON.parse(JSON.stringify($["/{queueName}/messages/{messageid}"].put)); - - from: swagger-document - where: $["x-ms-paths"]["/{queueName}/messages/{messageid}?updatevisibilityonly=true"] - transform: > - $.put.operationId = "Queue_UpdateMessageVisibility"; - $.put.parameters.shift(); -``` - -### Description - -```yaml -directive: - - from: swagger-document - where: $.definitions - transform: > - $.ApiVersion.description = "The version used for the operations to Azure storage services."; - $.GeoReplication.description = "Geo-Replication information for the Secondary Storage Service"; - $.Metadata.description = "A set of name-value pairs associated with this queue."; - $.CreateQueueResult.properties["Created"].description = "Indicates if the queue was successfully created by this operation."; - $.UpdateMessageResult.description = "Response type for #Azure::Storage::Queues::QueueClient::UpdateMessage."; - $.SetServicePropertiesResult.description = "Response type for #Azure::Storage::Queues::QueueServiceClient::SetProperties."; - - from: swagger-document - where: $["x-ms-paths"]["/{queueName}"].delete.responses["204"] - transform: > - $.schema.properties["Deleted"].description = "Indicates if the queue was successfully deleted by this operation."; - - from: swagger-document - where: $["x-ms-paths"]["/{queueName}?comp=metadata"].get.responses["200"].headers - transform: > - $["x-ms-meta"]["description"] = "A set of name-value pairs associated with this queue."; -``` \ No newline at end of file diff --git a/sdk/storage/azure-storage-queues/tsp-location.yaml b/sdk/storage/azure-storage-queues/tsp-location.yaml deleted file mode 100644 index eda1750d04..0000000000 --- a/sdk/storage/azure-storage-queues/tsp-location.yaml +++ /dev/null @@ -1,3 +0,0 @@ -directory: specification/storage/data-plane/QueueStorage -commit: ba3eef39e5be55cd5ed1a2e5c6c9bb1a17c36b9a -repo: Azure/azure-rest-api-specs diff --git a/sdk/storage/azure-storage-queues/tspconfig.yaml b/sdk/storage/azure-storage-queues/tspconfig.yaml deleted file mode 100644 index 08914785cb..0000000000 --- a/sdk/storage/azure-storage-queues/tspconfig.yaml +++ /dev/null @@ -1,11 +0,0 @@ -emit: - - "@azure-tools/typespec-cpp" -options: - "@azure-tools/typespec-cpp": - flavor: azure - namespace: Azure::Storage::Queues::_detail - headersOutputPath: azure/storage/queues - noPublicHeaders: true - pipelineMode: reuse - includeHeadersInResponse: true - api-version: "2026-04-06" diff --git a/sdk/storage/azure-storage-queues/typespec/README.md b/sdk/storage/azure-storage-queues/typespec/README.md deleted file mode 100644 index 63d673ef96..0000000000 --- a/sdk/storage/azure-storage-queues/typespec/README.md +++ /dev/null @@ -1,75 +0,0 @@ -# Queue protocol generation - -Queue operations are generated directly from TypeSpec using -[`Azure/typespec-cpp`](https://github.com/Azure/typespec-cpp). -`tsp-location.yaml` pins the service specification and `tspconfig.yaml` selects -the service version and internal protocol generation. - -`includeHeadersInResponse: true` explicitly enables the typed response -headers consumed by the compatibility layer. - -All generated code is in `src/generated`, under -`Azure::Storage::Queues::_detail`. The public clients reuse their existing -Storage pipelines and convert generated models to the existing public models. -The handwritten layer translates Core HTTP exceptions into `StorageException` -using the existing Storage error parser; no emitter-specific error factory is -needed. -An internal pipeline policy also removes the emitter's default `Accept` header -and retains `application/xml; charset=UTF-8` for XML requests before user policies -run. Requests without an XML body omit `Content-Type`, including visibility-only -message updates. This preserves the previous Queue wire behavior and compatibility -with existing recordings. User policies can still override these headers. -The public `inc/azure/storage/queues/rest_client.hpp` is retained for source -compatibility, including existing include paths, fields, defaults, and enums. -Do not replace it with the generated model header or edit generated C++ files. - -## Regeneration - -The emitter needs the Queue migration changes: XML metadata and nullable values, -header collections and scalar headers, SAS-safe query parameters, pipeline reuse, -and optional request bodies. Use merged emitter revision -`810b64ca434127b81547fbbabec039fe2d9dc4f2` -([Azure/typespec-cpp#585](https://github.com/Azure/typespec-cpp/pull/585)), -including the default-off response-header option, intrinsic optional ETags, and -fixed-precision XML date-times, and optional-body Content-Type handling. -Older published emitters are not sufficient. -Generate from a clean snapshot of that revision, not from uncommitted emitter -experiments. - -Build `packages/codegen.cpp` first, then `packages/typespec-cpp`, using the -emitter repository's package manifests. Use its compiler and dependencies rather -than independently installing the latest TypeSpec packages. The migration was -generated with compiler 1.12.0 and client-generator-core 0.68.4. - -Provide a QueueStorage specification directory from the exact revision in -`tsp-location.yaml`. Its `client.tsp` and `routes.tsp` contain the C++-scoped -customizations maintained in -[Azure/azure-rest-api-specs#47555](https://github.com/Azure/azure-rest-api-specs/pull/47555). -No package-local TypeSpec overlay is needed. From this package directory, run: - -```powershell -.\typespec\generate.ps1 ` - -EmitterPath C:\path\to\typespec-cpp\packages\typespec-cpp ` - -SpecDirectory C:\path\to\azure-rest-api-specs\specification\storage\data-plane\QueueStorage ` - -ClangFormatPath C:\path\to\clang-format-11\clang-format.exe -``` - -Use clang-format 11.0.0. The script stages the pinned service's four TypeSpec -files and generates directly from their `client.tsp` in a temporary -directory. It formats the output before copying the five generated files into -the package. `-OutputDirectory` can redirect the output for comparison without -changing the package. - -After regeneration, build the package and run the offline `QueueProtocolTest.*` -tests, then the existing playback tests when test-proxy assets are available. -The offline tests cover public model conversions, metadata, service versions, -SAS URLs, XML, paging, error handling, and visibility-only updates. - -The service's C++ projection preserves nullable RFC1123 statistics dates and 64-bit -receive/peek count options, supplies the `StorageServiceStats` XML root, and -enables metadata header collections. ACL dates retain all seven fractional-second -digits. Delegation-key request dates are projected as strings and formatted by -the handwritten client without fractional seconds, as required by that endpoint. -The pinned service applies `@alternateType(int64, "cpp")` directly to the -receive/peek count declarations in `routes.tsp`. These decorators affect only -the C++ projection; no JavaScript helper is needed. diff --git a/sdk/storage/azure-storage-queues/typespec/generate.ps1 b/sdk/storage/azure-storage-queues/typespec/generate.ps1 deleted file mode 100644 index 0b0362ab21..0000000000 --- a/sdk/storage/azure-storage-queues/typespec/generate.ps1 +++ /dev/null @@ -1,69 +0,0 @@ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. - -#Requires -Version 7.0 -[CmdletBinding()] -param( - [Parameter(Mandatory)] - [string] $EmitterPath, - - [Parameter(Mandatory)] - [string] $SpecDirectory, - - [string] $ClangFormatPath = "clang-format", - - [string] $OutputDirectory = (Split-Path $PSScriptRoot -Parent) -) - -$ErrorActionPreference = "Stop" -$emitter = (Resolve-Path -LiteralPath $EmitterPath).Path -$spec = (Resolve-Path -LiteralPath $SpecDirectory).Path -$compiler = Join-Path $emitter "node_modules\@typespec\compiler\cmd\tsp.js" -if (!(Test-Path -LiteralPath $compiler) -or !(Test-Path (Join-Path $emitter "dist\src\emitter.js"))) { - throw "Build packages\codegen.cpp and packages\typespec-cpp before generating." -} -$formatVersion = & $ClangFormatPath --version -if ($LASTEXITCODE -ne 0 -or $formatVersion -notmatch "clang-format version 11\.0\.0\b") { - throw "Generation requires clang-format 11.0.0." -} - -$work = Join-Path ([System.IO.Path]::GetTempPath()) ("queues-typespec-" + [guid]::NewGuid()) -$modules = Join-Path $work "node_modules" -New-Item -ItemType Directory -Path (Join-Path $work "service") | Out-Null -try { - foreach ($file in @("client.tsp", "main.tsp", "models.tsp", "routes.tsp")) { - Copy-Item -LiteralPath (Join-Path $spec $file) -Destination (Join-Path $work "service") - } - Copy-Item -LiteralPath (Join-Path (Split-Path $PSScriptRoot -Parent) "tspconfig.yaml") -Destination $work - Copy-Item -LiteralPath (Join-Path $PSScriptRoot "..\..\..\..\.clang-format") -Destination $work - $linkType = if ($IsWindows) { "Junction" } else { "SymbolicLink" } - New-Item -ItemType $linkType -Path $modules -Target (Join-Path $emitter "node_modules") | Out-Null - - & node $compiler compile (Join-Path $work "service\client.tsp") ` - --config (Join-Path $work "tspconfig.yaml") --emit $emitter ` - --output-dir (Join-Path $work "output") --pretty false - if ($LASTEXITCODE -ne 0) { - throw "Queue TypeSpec generation failed with exit code $LASTEXITCODE." - } - - $generated = Join-Path $work "output\src\generated" - $files = @("queues_client.cpp", "queues.cpp", "queues_client.hpp", - "queues_client_options.hpp", "queues_models.hpp") - foreach ($file in $files) { - & $ClangFormatPath -i --style=file (Join-Path $generated $file) - if ($LASTEXITCODE -ne 0) { - throw "Formatting $file failed with exit code $LASTEXITCODE." - } - } - $destination = Join-Path ([System.IO.Path]::GetFullPath($OutputDirectory)) "src\generated" - New-Item -ItemType Directory -Path $destination -Force | Out-Null - foreach ($file in $files) { - Copy-Item -LiteralPath (Join-Path $generated $file) -Destination $destination -Force - } -} -finally { - if (Test-Path -LiteralPath $modules) { - [System.IO.Directory]::Delete($modules) - } - Remove-Item -LiteralPath $work -Recurse -Force -} From a348eb83f84e9e0a68e657c41b759d4b83bee341 Mon Sep 17 00:00:00 2001 From: Jinming Hu Date: Sat, 24 Oct 2026 13:19:56 +1100 Subject: [PATCH 03/13] Replace Queue Swagger header with handwritten public models Preserve all 26 public model types and bitwise operators in queue_models.hpp. Remove obsolete private protocol declarations and relocate the unchanged default API version. Update public includes and packaging, and cover model defaults and enums. The removal of azure/storage/queues/rest_client.hpp is an explicitly accepted source-breaking include-path change; no forwarding header is retained. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../azure-storage-queues/CMakeLists.txt | 2 +- .../inc/azure/storage/queues.hpp | 2 +- .../inc/azure/storage/queues/queue_client.hpp | 4 + .../inc/azure/storage/queues/queue_models.hpp | 475 ++++++++++++ .../azure/storage/queues/queue_options.hpp | 6 +- .../azure/storage/queues/queue_responses.hpp | 2 +- .../inc/azure/storage/queues/rest_client.hpp | 727 ------------------ .../src/queue_sas_builder.cpp | 2 +- .../test/ut/CMakeLists.txt | 1 + .../test/ut/queue_models_test.cpp | 65 ++ 10 files changed, 554 insertions(+), 732 deletions(-) create mode 100644 sdk/storage/azure-storage-queues/inc/azure/storage/queues/queue_models.hpp delete mode 100644 sdk/storage/azure-storage-queues/inc/azure/storage/queues/rest_client.hpp create mode 100644 sdk/storage/azure-storage-queues/test/ut/queue_models_test.cpp diff --git a/sdk/storage/azure-storage-queues/CMakeLists.txt b/sdk/storage/azure-storage-queues/CMakeLists.txt index c17519a88b..62c290ebe0 100644 --- a/sdk/storage/azure-storage-queues/CMakeLists.txt +++ b/sdk/storage/azure-storage-queues/CMakeLists.txt @@ -44,11 +44,11 @@ set( inc/azure/storage/queues.hpp inc/azure/storage/queues/dll_import_export.hpp inc/azure/storage/queues/queue_client.hpp + inc/azure/storage/queues/queue_models.hpp inc/azure/storage/queues/queue_options.hpp inc/azure/storage/queues/queue_responses.hpp inc/azure/storage/queues/queue_sas_builder.hpp inc/azure/storage/queues/queue_service_client.hpp - inc/azure/storage/queues/rest_client.hpp inc/azure/storage/queues/rtti.hpp ) diff --git a/sdk/storage/azure-storage-queues/inc/azure/storage/queues.hpp b/sdk/storage/azure-storage-queues/inc/azure/storage/queues.hpp index a88551d86f..14b8bcdb6f 100644 --- a/sdk/storage/azure-storage-queues/inc/azure/storage/queues.hpp +++ b/sdk/storage/azure-storage-queues/inc/azure/storage/queues.hpp @@ -10,11 +10,11 @@ #include "azure/storage/queues/dll_import_export.hpp" #include "azure/storage/queues/queue_client.hpp" +#include "azure/storage/queues/queue_models.hpp" #include "azure/storage/queues/queue_options.hpp" #include "azure/storage/queues/queue_responses.hpp" #include "azure/storage/queues/queue_sas_builder.hpp" #include "azure/storage/queues/queue_service_client.hpp" -#include "azure/storage/queues/rest_client.hpp" #include "azure/storage/queues/rtti.hpp" #include diff --git a/sdk/storage/azure-storage-queues/inc/azure/storage/queues/queue_client.hpp b/sdk/storage/azure-storage-queues/inc/azure/storage/queues/queue_client.hpp index 64d173a5ac..35a9ef888b 100644 --- a/sdk/storage/azure-storage-queues/inc/azure/storage/queues/queue_client.hpp +++ b/sdk/storage/azure-storage-queues/inc/azure/storage/queues/queue_client.hpp @@ -11,7 +11,11 @@ #include "azure/storage/queues/queue_options.hpp" #include "azure/storage/queues/queue_responses.hpp" +#include #include +#include +#include +#include #include #include diff --git a/sdk/storage/azure-storage-queues/inc/azure/storage/queues/queue_models.hpp b/sdk/storage/azure-storage-queues/inc/azure/storage/queues/queue_models.hpp new file mode 100644 index 0000000000..a3c39bb9b6 --- /dev/null +++ b/sdk/storage/azure-storage-queues/inc/azure/storage/queues/queue_models.hpp @@ -0,0 +1,475 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +/** + * @file + * @brief Defines Queue service models and operation results. + */ + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace Azure { namespace Storage { namespace Queues { namespace Models { + /** + * @brief The retention policy. + */ + struct RetentionPolicy final + { + /** + * Indicates whether a retention policy is enabled for the storage service. + */ + bool IsEnabled = bool(); + /** + * Indicates the number of days that metrics or logging or soft-deleted data should be + * retained. All data older than this value will be deleted. + */ + Nullable Days; + }; + /** + * @brief Azure Analytics Logging settings. + */ + struct AnalyticsLogging final + { + /** + * The version of Storage Analytics to configure. + */ + std::string Version; + /** + * Indicates whether all delete requests should be logged. + */ + bool Delete = bool(); + /** + * Indicates whether all read requests should be logged. + */ + bool Read = bool(); + /** + * Indicates whether all write requests should be logged. + */ + bool Write = bool(); + /** + * The retention policy. + */ + Models::RetentionPolicy RetentionPolicy; + }; + /** + * @brief A summary of request statistics grouped by API in hour or minute aggregates for + * queues. + */ + struct Metrics final + { + /** + * The version of Storage Analytics to configure. + */ + std::string Version; + /** + * Indicates whether metrics are enabled for the Queue service. + */ + bool IsEnabled = bool(); + /** + * Indicates whether metrics should generate summary statistics for called API operations. + */ + Nullable IncludeApis; + /** + * The retention policy. + */ + Models::RetentionPolicy RetentionPolicy; + }; + /** + * @brief CORS is an HTTP feature that enables a web application running under one domain to + * access resources in another domain. Web browsers implement a security restriction known as + * same-origin policy that prevents a web page from calling APIs in a different domain; CORS + * provides a secure way to allow one domain (the origin domain) to call APIs in another domain. + */ + struct CorsRule final + { + /** + * The origin domains that are permitted to make a request against the storage service via + * CORS. The origin domain is the domain from which the request originates. Note that the + * origin must be an exact case-sensitive match with the origin that the user age sends to the + * service. You can also use the wildcard character '*' to allow all origin domains to make + * requests via CORS. + */ + std::string AllowedOrigins; + /** + * The methods (HTTP request verbs) that the origin domain may use for a CORS request. (comma + * separated). + */ + std::string AllowedMethods; + /** + * The request headers that the origin domain may specify on the CORS request. + */ + std::string AllowedHeaders; + /** + * The response headers that may be sent in the response to the CORS request and exposed by + * the browser to the request issuer. + */ + std::string ExposedHeaders; + /** + * The maximum amount time that a browser should cache the preflight OPTIONS request. + */ + std::int32_t MaxAgeInSeconds = std::int32_t(); + }; + /** + * @brief Response type for #Azure::Storage::Queues::QueueServiceClient::SetProperties. + */ + struct SetServicePropertiesResult final + { + }; + /** + * @brief Storage Service Properties. + */ + struct QueueServiceProperties final + { + /** + * Azure Analytics Logging settings. + */ + AnalyticsLogging Logging; + /** + * A summary of request statistics grouped by API in hourly aggregates for queues. + */ + Metrics HourMetrics; + /** + * A summary of request statistics grouped by API in minute aggregates for queues. + */ + Metrics MinuteMetrics; + /** + * The set of CORS rules. + */ + std::vector Cors; + }; + /** + * @brief The status of the secondary location. + */ + class GeoReplicationStatus final + : public Core::_internal::ExtendableEnumeration { + public: + /** Constructs a new GeoReplicationStatus instance */ + GeoReplicationStatus() = default; + /** Constructs a new GeoReplicationStatus from a string. */ + explicit GeoReplicationStatus(std::string value) : ExtendableEnumeration(std::move(value)) {} + + /** Constant value of type GeoReplicationStatus: Live */ + AZ_STORAGE_QUEUES_DLLEXPORT const static GeoReplicationStatus Live; + /** Constant value of type GeoReplicationStatus: Bootstrap */ + AZ_STORAGE_QUEUES_DLLEXPORT const static GeoReplicationStatus Bootstrap; + /** Constant value of type GeoReplicationStatus: Unavailable */ + AZ_STORAGE_QUEUES_DLLEXPORT const static GeoReplicationStatus Unavailable; + }; + /** + * @brief Geo-Replication information for the Secondary Storage Service. + */ + struct GeoReplication final + { + /** + * The status of the secondary location. + */ + GeoReplicationStatus Status; + /** + * A GMT date/time value, to the second. All primary writes preceding this value are + * guaranteed to be available for read operations at the secondary. Primary writes after this + * point in time may or may not be available for reads. + */ + Nullable LastSyncedOn; + }; + /** + * @brief Stats for the storage service. + */ + struct ServiceStatistics final + { + /** + * Geo-Replication information for the Secondary Storage Service. + */ + Models::GeoReplication GeoReplication; + }; + /** + * @brief A user delegation key. + */ + struct UserDelegationKey final + { + /** + * The Azure Active Directory object ID in GUID format. + */ + std::string SignedObjectId; + /** + * The Azure Active Directory tenant ID in GUID format. + */ + std::string SignedTenantId; + /** + * The date-time the key is active. + */ + DateTime SignedStartsOn; + /** + * The date-time the key expires. + */ + DateTime SignedExpiresOn; + /** + * Abbreviation of the Azure Storage service that accepts the key. + */ + std::string SignedService; + /** + * The service version that created the key. + */ + std::string SignedVersion; + /** + * The delegated user tenant id in Azure AD. Return if DelegatedUserTid is specified. + */ + Nullable SignedDelegatedUserTid; + /** + * The key as a base64 string. + */ + std::string Value; + }; + /** + * @brief An Azure Storage Queue. + */ + struct QueueItem final + { + /** + * The name of the Queue. + */ + std::string Name; + /** + * A set of name-value pairs associated with this queue. + */ + Core::CaseInsensitiveMap Metadata; + }; + /** + * @brief Include this parameter to specify that the queues' metadata be returned as part of the + * response body. + */ + enum class ListQueuesIncludeFlags + { + None = 0, + Metadata = 1, + }; + inline ListQueuesIncludeFlags operator|(ListQueuesIncludeFlags lhs, ListQueuesIncludeFlags rhs) + { + using type = std::underlying_type_t; + return static_cast(static_cast(lhs) | static_cast(rhs)); + } + inline ListQueuesIncludeFlags& operator|=(ListQueuesIncludeFlags& lhs, ListQueuesIncludeFlags rhs) + { + lhs = lhs | rhs; + return lhs; + } + inline ListQueuesIncludeFlags operator&(ListQueuesIncludeFlags lhs, ListQueuesIncludeFlags rhs) + { + using type = std::underlying_type_t; + return static_cast(static_cast(lhs) & static_cast(rhs)); + } + inline ListQueuesIncludeFlags& operator&=(ListQueuesIncludeFlags& lhs, ListQueuesIncludeFlags rhs) + { + lhs = lhs & rhs; + return lhs; + } + /** + * @brief Response type for #Azure::Storage::Queues::QueueClient::Create. + */ + struct CreateQueueResult final + { + /** + * Indicates if the queue was successfully created by this operation. + */ + bool Created = bool(); + }; + /** + * @brief Response type for #Azure::Storage::Queues::QueueClient::Delete. + */ + struct DeleteQueueResult final + { + /** + * Indicates if the queue was successfully deleted by this operation. + */ + bool Deleted = true; + }; + /** + * @brief Response type for #Azure::Storage::Queues::QueueClient::SetMetadata. + */ + struct SetQueueMetadataResult final + { + }; + /** + * @brief Signed identifier. + */ + struct SignedIdentifier final + { + /** + * A unique id. + */ + std::string Id; + /** + * The date-time the policy is active. + */ + Nullable StartsOn; + /** + * The date-time the policy expires. + */ + Nullable ExpiresOn; + /** + * The permissions for the acl policy. + */ + std::string Permissions; + }; + /** + * @brief Response type for #Azure::Storage::Queues::QueueClient::GetAccessPolicy. + */ + struct QueueAccessPolicy final + { + /** + * A collection of signed identifiers. + */ + std::vector SignedIdentifiers; + }; + /** + * @brief Response type for #Azure::Storage::Queues::QueueClient::SetAccessPolicy. + */ + struct SetQueueAccessPolicyResult final + { + }; + /** + * @brief The object returned in the QueueMessageList array when calling Get Messages on a + * Queue. + */ + struct QueueMessage final + { + /** + * The Id of the Message. + */ + std::string MessageId; + /** + * The time the Message was inserted into the Queue. + */ + DateTime InsertedOn; + /** + * The time that the Message will expire and be automatically deleted. + */ + DateTime ExpiresOn; + /** + * This value is required to delete the Message. If deletion fails using this popreceipt then + * the message has been dequeued by another client. + */ + std::string PopReceipt; + /** + * The time that the message will again become visible in the Queue. + */ + DateTime NextVisibleOn; + /** + * The number of times the message has been dequeued. + */ + std::int64_t DequeueCount = std::int64_t(); + /** + * The content of the Message. + */ + std::string MessageText; + }; + /** + * @brief Response type for #Azure::Storage::Queues::QueueClient::ReceiveMessages. + */ + struct ReceivedMessages final + { + /** + * The object returned when calling Get Messages on a Queue. + */ + std::vector Messages; + }; + /** + * @brief Response type for #Azure::Storage::Queues::QueueClient::ClearMessages. + */ + struct ClearMessagesResult final + { + }; + /** + * @brief The object returned in the QueueMessageList array when calling Put Message on a Queue. + */ + struct EnqueueMessageResult final + { + /** + * The Id of the Message. + */ + std::string MessageId; + /** + * The time the Message was inserted into the Queue. + */ + DateTime InsertedOn; + /** + * The time that the Message will expire and be automatically deleted. + */ + DateTime ExpiresOn; + /** + * This value is required to delete the Message. If deletion fails using this popreceipt then + * the message has been dequeued by another client. + */ + std::string PopReceipt; + /** + * The time that the message will again become visible in the Queue. + */ + DateTime NextVisibleOn; + }; + /** + * @brief The object returned in the QueueMessageList array when calling Peek Messages on a + * Queue. + */ + struct PeekedQueueMessage final + { + /** + * The Id of the Message. + */ + std::string MessageId; + /** + * The time the Message was inserted into the Queue. + */ + DateTime InsertedOn; + /** + * The time that the Message will expire and be automatically deleted. + */ + DateTime ExpiresOn; + /** + * The number of times the message has been dequeued. + */ + std::int64_t DequeueCount = std::int64_t(); + /** + * The content of the Message. + */ + std::string MessageText; + }; + /** + * @brief Response type for #Azure::Storage::Queues::QueueClient::PeekMessages. + */ + struct PeekedMessages final + { + /** + * The object returned when calling Peek Messages on a Queue. + */ + std::vector Messages; + }; + /** + * @brief Response type for #Azure::Storage::Queues::QueueClient::DeleteMessage. + */ + struct DeleteMessageResult final + { + }; + /** + * @brief Response type for #Azure::Storage::Queues::QueueClient::UpdateMessage. + */ + struct UpdateMessageResult final + { + /** + * The pop receipt of the queue message. + */ + std::string PopReceipt; + /** + * A UTC date/time value that represents when the message will be visible on the queue. + */ + DateTime NextVisibleOn; + }; +}}}} // namespace Azure::Storage::Queues::Models \ No newline at end of file diff --git a/sdk/storage/azure-storage-queues/inc/azure/storage/queues/queue_options.hpp b/sdk/storage/azure-storage-queues/inc/azure/storage/queues/queue_options.hpp index 3a10cf22fc..7005780a56 100644 --- a/sdk/storage/azure-storage-queues/inc/azure/storage/queues/queue_options.hpp +++ b/sdk/storage/azure-storage-queues/inc/azure/storage/queues/queue_options.hpp @@ -8,7 +8,7 @@ #pragma once -#include "azure/storage/queues/rest_client.hpp" +#include "azure/storage/queues/queue_models.hpp" #include #include @@ -19,6 +19,10 @@ namespace Azure { namespace Storage { namespace Queues { + namespace _detail { + constexpr static const char* ApiVersion = "2026-04-06"; + } // namespace _detail + /** * @brief Audiences available for queue service * diff --git a/sdk/storage/azure-storage-queues/inc/azure/storage/queues/queue_responses.hpp b/sdk/storage/azure-storage-queues/inc/azure/storage/queues/queue_responses.hpp index 31cb3f260b..ef893363af 100644 --- a/sdk/storage/azure-storage-queues/inc/azure/storage/queues/queue_responses.hpp +++ b/sdk/storage/azure-storage-queues/inc/azure/storage/queues/queue_responses.hpp @@ -8,8 +8,8 @@ #pragma once +#include "azure/storage/queues/queue_models.hpp" #include "azure/storage/queues/queue_options.hpp" -#include "azure/storage/queues/rest_client.hpp" #include diff --git a/sdk/storage/azure-storage-queues/inc/azure/storage/queues/rest_client.hpp b/sdk/storage/azure-storage-queues/inc/azure/storage/queues/rest_client.hpp deleted file mode 100644 index 8f4fd815d5..0000000000 --- a/sdk/storage/azure-storage-queues/inc/azure/storage/queues/rest_client.hpp +++ /dev/null @@ -1,727 +0,0 @@ - -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -// -// Code generated by Microsoft (R) AutoRest C++ Code Generator. -// Changes may cause incorrect behavior and will be lost if the code is regenerated. -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -namespace Azure { namespace Storage { namespace Queues { - namespace _detail { - /** - * The version used for the operations to Azure storage services. - */ - constexpr static const char* ApiVersion = "2026-04-06"; - } // namespace _detail - namespace Models { - /** - * @brief The retention policy. - */ - struct RetentionPolicy final - { - /** - * Indicates whether a retention policy is enabled for the storage service. - */ - bool IsEnabled = bool(); - /** - * Indicates the number of days that metrics or logging or soft-deleted data should be - * retained. All data older than this value will be deleted. - */ - Nullable Days; - }; - /** - * @brief Azure Analytics Logging settings. - */ - struct AnalyticsLogging final - { - /** - * The version of Storage Analytics to configure. - */ - std::string Version; - /** - * Indicates whether all delete requests should be logged. - */ - bool Delete = bool(); - /** - * Indicates whether all read requests should be logged. - */ - bool Read = bool(); - /** - * Indicates whether all write requests should be logged. - */ - bool Write = bool(); - /** - * The retention policy. - */ - Models::RetentionPolicy RetentionPolicy; - }; - /** - * @brief A summary of request statistics grouped by API in hour or minute aggregates for - * queues. - */ - struct Metrics final - { - /** - * The version of Storage Analytics to configure. - */ - std::string Version; - /** - * Indicates whether metrics are enabled for the Queue service. - */ - bool IsEnabled = bool(); - /** - * Indicates whether metrics should generate summary statistics for called API operations. - */ - Nullable IncludeApis; - /** - * The retention policy. - */ - Models::RetentionPolicy RetentionPolicy; - }; - /** - * @brief CORS is an HTTP feature that enables a web application running under one domain to - * access resources in another domain. Web browsers implement a security restriction known as - * same-origin policy that prevents a web page from calling APIs in a different domain; CORS - * provides a secure way to allow one domain (the origin domain) to call APIs in another domain. - */ - struct CorsRule final - { - /** - * The origin domains that are permitted to make a request against the storage service via - * CORS. The origin domain is the domain from which the request originates. Note that the - * origin must be an exact case-sensitive match with the origin that the user age sends to the - * service. You can also use the wildcard character '*' to allow all origin domains to make - * requests via CORS. - */ - std::string AllowedOrigins; - /** - * The methods (HTTP request verbs) that the origin domain may use for a CORS request. (comma - * separated). - */ - std::string AllowedMethods; - /** - * The request headers that the origin domain may specify on the CORS request. - */ - std::string AllowedHeaders; - /** - * The response headers that may be sent in the response to the CORS request and exposed by - * the browser to the request issuer. - */ - std::string ExposedHeaders; - /** - * The maximum amount time that a browser should cache the preflight OPTIONS request. - */ - std::int32_t MaxAgeInSeconds = std::int32_t(); - }; - /** - * @brief Response type for #Azure::Storage::Queues::QueueServiceClient::SetProperties. - */ - struct SetServicePropertiesResult final - { - }; - /** - * @brief Storage Service Properties. - */ - struct QueueServiceProperties final - { - /** - * Azure Analytics Logging settings. - */ - AnalyticsLogging Logging; - /** - * A summary of request statistics grouped by API in hourly aggregates for queues. - */ - Metrics HourMetrics; - /** - * A summary of request statistics grouped by API in minute aggregates for queues. - */ - Metrics MinuteMetrics; - /** - * The set of CORS rules. - */ - std::vector Cors; - }; - /** - * @brief The status of the secondary location. - */ - class GeoReplicationStatus final - : public Core::_internal::ExtendableEnumeration { - public: - /** Constructs a new GeoReplicationStatus instance */ - GeoReplicationStatus() = default; - /** Constructs a new GeoReplicationStatus from a string. */ - explicit GeoReplicationStatus(std::string value) : ExtendableEnumeration(std::move(value)) {} - - /** Constant value of type GeoReplicationStatus: Live */ - AZ_STORAGE_QUEUES_DLLEXPORT const static GeoReplicationStatus Live; - /** Constant value of type GeoReplicationStatus: Bootstrap */ - AZ_STORAGE_QUEUES_DLLEXPORT const static GeoReplicationStatus Bootstrap; - /** Constant value of type GeoReplicationStatus: Unavailable */ - AZ_STORAGE_QUEUES_DLLEXPORT const static GeoReplicationStatus Unavailable; - }; - /** - * @brief Geo-Replication information for the Secondary Storage Service. - */ - struct GeoReplication final - { - /** - * The status of the secondary location. - */ - GeoReplicationStatus Status; - /** - * A GMT date/time value, to the second. All primary writes preceding this value are - * guaranteed to be available for read operations at the secondary. Primary writes after this - * point in time may or may not be available for reads. - */ - Nullable LastSyncedOn; - }; - /** - * @brief Stats for the storage service. - */ - struct ServiceStatistics final - { - /** - * Geo-Replication information for the Secondary Storage Service. - */ - Models::GeoReplication GeoReplication; - }; - namespace _detail { - /** - * @brief Key information. - */ - struct KeyInfo final - { - /** - * The date-time the key is active in ISO 8601 UTC time. - */ - Nullable Start; - /** - * The date-time the key expires in ISO 8601 UTC time. - */ - std::string Expiry; - /** - * The delegated user tenant id in Azure AD. - */ - Nullable DelegatedUserTid; - }; - } // namespace _detail - /** - * @brief A user delegation key. - */ - struct UserDelegationKey final - { - /** - * The Azure Active Directory object ID in GUID format. - */ - std::string SignedObjectId; - /** - * The Azure Active Directory tenant ID in GUID format. - */ - std::string SignedTenantId; - /** - * The date-time the key is active. - */ - DateTime SignedStartsOn; - /** - * The date-time the key expires. - */ - DateTime SignedExpiresOn; - /** - * Abbreviation of the Azure Storage service that accepts the key. - */ - std::string SignedService; - /** - * The service version that created the key. - */ - std::string SignedVersion; - /** - * The delegated user tenant id in Azure AD. Return if DelegatedUserTid is specified. - */ - Nullable SignedDelegatedUserTid; - /** - * The key as a base64 string. - */ - std::string Value; - }; - /** - * @brief An Azure Storage Queue. - */ - struct QueueItem final - { - /** - * The name of the Queue. - */ - std::string Name; - /** - * A set of name-value pairs associated with this queue. - */ - Core::CaseInsensitiveMap Metadata; - }; - /** - * @brief Include this parameter to specify that the queues' metadata be returned as part of the - * response body. - */ - enum class ListQueuesIncludeFlags - { - None = 0, - Metadata = 1, - }; - inline ListQueuesIncludeFlags operator|(ListQueuesIncludeFlags lhs, ListQueuesIncludeFlags rhs) - { - using type = std::underlying_type_t; - return static_cast(static_cast(lhs) | static_cast(rhs)); - } - inline ListQueuesIncludeFlags& operator|=( - ListQueuesIncludeFlags& lhs, - ListQueuesIncludeFlags rhs) - { - lhs = lhs | rhs; - return lhs; - } - inline ListQueuesIncludeFlags operator&(ListQueuesIncludeFlags lhs, ListQueuesIncludeFlags rhs) - { - using type = std::underlying_type_t; - return static_cast(static_cast(lhs) & static_cast(rhs)); - } - inline ListQueuesIncludeFlags& operator&=( - ListQueuesIncludeFlags& lhs, - ListQueuesIncludeFlags rhs) - { - lhs = lhs & rhs; - return lhs; - } - namespace _detail { - /** - * @brief The object returned when calling List Queues on a Queue Service. - */ - struct ListQueuesResult final - { - std::string ServiceEndpoint; - std::string Prefix; - /** - * Array of QueueItem. - */ - std::vector Items; - Nullable ContinuationToken; - }; - } // namespace _detail - /** - * @brief Response type for #Azure::Storage::Queues::QueueClient::Create. - */ - struct CreateQueueResult final - { - /** - * Indicates if the queue was successfully created by this operation. - */ - bool Created = bool(); - }; - /** - * @brief Response type for #Azure::Storage::Queues::QueueClient::Delete. - */ - struct DeleteQueueResult final - { - /** - * Indicates if the queue was successfully deleted by this operation. - */ - 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. - */ - 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. - */ - struct SetQueueMetadataResult final - { - }; - /** - * @brief Signed identifier. - */ - struct SignedIdentifier final - { - /** - * A unique id. - */ - std::string Id; - /** - * The date-time the policy is active. - */ - Nullable StartsOn; - /** - * The date-time the policy expires. - */ - Nullable ExpiresOn; - /** - * The permissions for the acl policy. - */ - std::string Permissions; - }; - /** - * @brief Response type for #Azure::Storage::Queues::QueueClient::GetAccessPolicy. - */ - struct QueueAccessPolicy final - { - /** - * A collection of signed identifiers. - */ - std::vector SignedIdentifiers; - }; - /** - * @brief Response type for #Azure::Storage::Queues::QueueClient::SetAccessPolicy. - */ - struct SetQueueAccessPolicyResult final - { - }; - /** - * @brief The object returned in the QueueMessageList array when calling Get Messages on a - * Queue. - */ - struct QueueMessage final - { - /** - * The Id of the Message. - */ - std::string MessageId; - /** - * The time the Message was inserted into the Queue. - */ - DateTime InsertedOn; - /** - * The time that the Message will expire and be automatically deleted. - */ - DateTime ExpiresOn; - /** - * This value is required to delete the Message. If deletion fails using this popreceipt then - * the message has been dequeued by another client. - */ - std::string PopReceipt; - /** - * The time that the message will again become visible in the Queue. - */ - DateTime NextVisibleOn; - /** - * The number of times the message has been dequeued. - */ - std::int64_t DequeueCount = std::int64_t(); - /** - * The content of the Message. - */ - std::string MessageText; - }; - /** - * @brief Response type for #Azure::Storage::Queues::QueueClient::ReceiveMessages. - */ - struct ReceivedMessages final - { - /** - * The object returned when calling Get Messages on a Queue. - */ - std::vector Messages; - }; - /** - * @brief Response type for #Azure::Storage::Queues::QueueClient::ClearMessages. - */ - struct ClearMessagesResult final - { - }; - namespace _detail { - /** - * @brief A Message object which can be stored in a Queue. - */ - struct QueueMessageInternal final - { - /** - * The content of the message. - */ - std::string MessageText; - }; - } // namespace _detail - /** - * @brief The object returned in the QueueMessageList array when calling Put Message on a Queue. - */ - struct EnqueueMessageResult final - { - /** - * The Id of the Message. - */ - std::string MessageId; - /** - * The time the Message was inserted into the Queue. - */ - DateTime InsertedOn; - /** - * The time that the Message will expire and be automatically deleted. - */ - DateTime ExpiresOn; - /** - * This value is required to delete the Message. If deletion fails using this popreceipt then - * the message has been dequeued by another client. - */ - std::string PopReceipt; - /** - * The time that the message will again become visible in the Queue. - */ - DateTime NextVisibleOn; - }; - /** - * @brief The object returned in the QueueMessageList array when calling Peek Messages on a - * Queue. - */ - struct PeekedQueueMessage final - { - /** - * The Id of the Message. - */ - std::string MessageId; - /** - * The time the Message was inserted into the Queue. - */ - DateTime InsertedOn; - /** - * The time that the Message will expire and be automatically deleted. - */ - DateTime ExpiresOn; - /** - * The number of times the message has been dequeued. - */ - std::int64_t DequeueCount = std::int64_t(); - /** - * The content of the Message. - */ - std::string MessageText; - }; - /** - * @brief Response type for #Azure::Storage::Queues::QueueClient::PeekMessages. - */ - struct PeekedMessages final - { - /** - * The object returned when calling Peek Messages on a Queue. - */ - std::vector Messages; - }; - /** - * @brief Response type for #Azure::Storage::Queues::QueueClient::DeleteMessage. - */ - struct DeleteMessageResult final - { - }; - /** - * @brief Response type for #Azure::Storage::Queues::QueueClient::UpdateMessage. - */ - struct UpdateMessageResult final - { - /** - * The pop receipt of the queue message. - */ - std::string PopReceipt; - /** - * A UTC date/time value that represents when the message will be visible on the queue. - */ - DateTime NextVisibleOn; - }; - } // namespace Models - namespace _detail { - class ServiceClient final { - public: - struct SetServicePropertiesOptions final - { - Models::QueueServiceProperties QueueServiceProperties; - }; - static Response SetProperties( - Core::Http::_internal::HttpPipeline& pipeline, - const Core::Url& url, - const SetServicePropertiesOptions& options, - const Core::Context& context); - struct GetServicePropertiesOptions final - { - }; - static Response GetProperties( - Core::Http::_internal::HttpPipeline& pipeline, - const Core::Url& url, - const GetServicePropertiesOptions& options, - const Core::Context& context); - struct GetServiceStatisticsOptions final - { - }; - static Response GetStatistics( - Core::Http::_internal::HttpPipeline& pipeline, - const Core::Url& url, - const GetServiceStatisticsOptions& options, - const Core::Context& context); - struct GetServiceUserDelegationKeyOptions final - { - Models::_detail::KeyInfo KeyInfo; - }; - static Response GetUserDelegationKey( - Core::Http::_internal::HttpPipeline& pipeline, - const Core::Url& url, - const GetServiceUserDelegationKeyOptions& options, - const Core::Context& context); - struct ListServiceQueuesSegmentOptions final - { - Nullable Prefix; - Nullable Marker; - Nullable MaxResults; - Nullable Include; - }; - static Response ListQueuesSegment( - Core::Http::_internal::HttpPipeline& pipeline, - const Core::Url& url, - const ListServiceQueuesSegmentOptions& options, - const Core::Context& context); - }; - class QueueClient final { - public: - struct CreateQueueOptions final - { - std::map Metadata; - }; - static Response Create( - Core::Http::_internal::HttpPipeline& pipeline, - const Core::Url& url, - const CreateQueueOptions& options, - const Core::Context& context); - struct DeleteQueueOptions final - { - }; - static Response Delete( - Core::Http::_internal::HttpPipeline& pipeline, - const Core::Url& url, - const DeleteQueueOptions& options, - const Core::Context& context); - struct GetQueuePropertiesOptions final - { - }; - static Response GetProperties( - Core::Http::_internal::HttpPipeline& pipeline, - const Core::Url& url, - const GetQueuePropertiesOptions& options, - const Core::Context& context); - struct SetQueueMetadataOptions final - { - std::map Metadata; - }; - static Response SetMetadata( - Core::Http::_internal::HttpPipeline& pipeline, - const Core::Url& url, - const SetQueueMetadataOptions& options, - const Core::Context& context); - struct GetQueueAccessPolicyOptions final - { - }; - static Response GetAccessPolicy( - Core::Http::_internal::HttpPipeline& pipeline, - const Core::Url& url, - const GetQueueAccessPolicyOptions& options, - const Core::Context& context); - struct SetQueueAccessPolicyOptions final - { - std::vector QueueAcl; - }; - static Response SetAccessPolicy( - Core::Http::_internal::HttpPipeline& pipeline, - const Core::Url& url, - const SetQueueAccessPolicyOptions& options, - const Core::Context& context); - struct ReceiveQueueMessagesOptions final - { - Nullable NumberOfMessages; - Nullable Visibilitytimeout; - }; - static Response ReceiveMessages( - Core::Http::_internal::HttpPipeline& pipeline, - const Core::Url& url, - const ReceiveQueueMessagesOptions& options, - const Core::Context& context); - struct ClearQueueMessagesOptions final - { - }; - static Response ClearMessages( - Core::Http::_internal::HttpPipeline& pipeline, - const Core::Url& url, - const ClearQueueMessagesOptions& options, - const Core::Context& context); - struct EnqueueQueueMessageOptions final - { - Models::_detail::QueueMessageInternal QueueMessage; - Nullable Visibilitytimeout; - Nullable MessageTimeToLive; - }; - static Response EnqueueMessage( - Core::Http::_internal::HttpPipeline& pipeline, - const Core::Url& url, - const EnqueueQueueMessageOptions& options, - const Core::Context& context); - struct PeekQueueMessagesOptions final - { - Nullable NumberOfMessages; - }; - static Response PeekMessages( - Core::Http::_internal::HttpPipeline& pipeline, - const Core::Url& url, - const PeekQueueMessagesOptions& options, - const Core::Context& context); - struct UpdateQueueMessageOptions final - { - Models::_detail::QueueMessageInternal QueueMessage; - std::string PopReceipt; - std::int32_t Visibilitytimeout = std::int32_t(); - }; - static Response UpdateMessage( - Core::Http::_internal::HttpPipeline& pipeline, - const Core::Url& url, - const UpdateQueueMessageOptions& options, - const Core::Context& context); - struct DeleteQueueMessageOptions final - { - std::string PopReceipt; - }; - static Response DeleteMessage( - Core::Http::_internal::HttpPipeline& pipeline, - const Core::Url& url, - const DeleteQueueMessageOptions& options, - const Core::Context& context); - struct UpdateQueueMessageVisibilityOptions final - { - std::string PopReceipt; - std::int32_t Visibilitytimeout = std::int32_t(); - }; - static Response UpdateMessageVisibility( - Core::Http::_internal::HttpPipeline& pipeline, - const Core::Url& url, - const UpdateQueueMessageVisibilityOptions& options, - const Core::Context& context); - }; - } // namespace _detail -}}} // namespace Azure::Storage::Queues \ No newline at end of file diff --git a/sdk/storage/azure-storage-queues/src/queue_sas_builder.cpp b/sdk/storage/azure-storage-queues/src/queue_sas_builder.cpp index c53be91ec4..795c81a5dd 100644 --- a/sdk/storage/azure-storage-queues/src/queue_sas_builder.cpp +++ b/sdk/storage/azure-storage-queues/src/queue_sas_builder.cpp @@ -3,7 +3,7 @@ #include "azure/storage/queues/queue_sas_builder.hpp" -#include "azure/storage/queues/rest_client.hpp" +#include "azure/storage/queues/queue_options.hpp" #include #include diff --git a/sdk/storage/azure-storage-queues/test/ut/CMakeLists.txt b/sdk/storage/azure-storage-queues/test/ut/CMakeLists.txt index d3fd1227a9..b9efd48426 100644 --- a/sdk/storage/azure-storage-queues/test/ut/CMakeLists.txt +++ b/sdk/storage/azure-storage-queues/test/ut/CMakeLists.txt @@ -20,6 +20,7 @@ add_executable ( queue_client_messages_test.cpp queue_client_test.cpp queue_client_test.hpp + queue_models_test.cpp queue_protocol_test.cpp queue_sas_test.cpp queue_service_client_test.cpp diff --git a/sdk/storage/azure-storage-queues/test/ut/queue_models_test.cpp b/sdk/storage/azure-storage-queues/test/ut/queue_models_test.cpp new file mode 100644 index 0000000000..3d12a4945d --- /dev/null +++ b/sdk/storage/azure-storage-queues/test/ut/queue_models_test.cpp @@ -0,0 +1,65 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include + +#include + +namespace Azure { namespace Storage { namespace Test { + + TEST(QueueModels, Defaults) + { + namespace Models = Queues::Models; + Models::RetentionPolicy retention; + EXPECT_FALSE(retention.IsEnabled); + EXPECT_FALSE(retention.Days.HasValue()); + Models::AnalyticsLogging logging; + EXPECT_TRUE(logging.Version.empty()); + EXPECT_FALSE(logging.Delete); + EXPECT_FALSE(logging.Read); + EXPECT_FALSE(logging.Write); + EXPECT_FALSE(logging.RetentionPolicy.IsEnabled); + Models::Metrics metrics; + EXPECT_TRUE(metrics.Version.empty()); + EXPECT_FALSE(metrics.IsEnabled); + EXPECT_FALSE(metrics.IncludeApis.HasValue()); + EXPECT_FALSE(metrics.RetentionPolicy.IsEnabled); + EXPECT_EQ(Models::CorsRule().MaxAgeInSeconds, 0); + EXPECT_TRUE(Models::QueueServiceProperties().Cors.empty()); + EXPECT_TRUE(Models::GeoReplicationStatus().ToString().empty()); + EXPECT_FALSE(Models::GeoReplication().LastSyncedOn.HasValue()); + EXPECT_FALSE(Models::UserDelegationKey().SignedDelegatedUserTid.HasValue()); + EXPECT_TRUE(Models::QueueItem().Metadata.empty()); + EXPECT_FALSE(Models::CreateQueueResult().Created); + EXPECT_TRUE(Models::DeleteQueueResult().Deleted); + Models::SignedIdentifier identifier; + EXPECT_FALSE(identifier.StartsOn.HasValue()); + EXPECT_FALSE(identifier.ExpiresOn.HasValue()); + EXPECT_TRUE(identifier.Permissions.empty()); + EXPECT_TRUE(Models::QueueAccessPolicy().SignedIdentifiers.empty()); + EXPECT_EQ(Models::QueueMessage().DequeueCount, 0); + EXPECT_TRUE(Models::ReceivedMessages().Messages.empty()); + EXPECT_EQ(Models::PeekedQueueMessage().DequeueCount, 0); + EXPECT_TRUE(Models::PeekedMessages().Messages.empty()); + } + + TEST(QueueModels, Enumerations) + { + using Queues::Models::GeoReplicationStatus; + using Queues::Models::ListQueuesIncludeFlags; + EXPECT_EQ(GeoReplicationStatus::Live, GeoReplicationStatus("live")); + EXPECT_EQ(GeoReplicationStatus::Bootstrap, GeoReplicationStatus("bootstrap")); + EXPECT_EQ(GeoReplicationStatus::Unavailable, GeoReplicationStatus("unavailable")); + EXPECT_EQ(GeoReplicationStatus("custom").ToString(), "custom"); + auto flags = ListQueuesIncludeFlags::None; + EXPECT_EQ(static_cast(flags), 0); + EXPECT_EQ(static_cast(ListQueuesIncludeFlags::Metadata), 1); + flags |= ListQueuesIncludeFlags::Metadata; + EXPECT_EQ(flags, ListQueuesIncludeFlags::Metadata); + EXPECT_EQ(flags & ListQueuesIncludeFlags::Metadata, ListQueuesIncludeFlags::Metadata); + EXPECT_EQ(flags | ListQueuesIncludeFlags::None, ListQueuesIncludeFlags::Metadata); + flags &= ListQueuesIncludeFlags::None; + EXPECT_EQ(flags, ListQueuesIncludeFlags::None); + } + +}}} // namespace Azure::Storage::Test From cd689c4d236a19123c33ca77bd13d06f353dbe3a Mon Sep 17 00:00:00 2001 From: Jinming Hu Date: Sat, 24 Oct 2026 13:27:35 +1100 Subject: [PATCH 04/13] Consolidate Queue public models in response header Move preserved public model declarations and operators into queue_responses.hpp and remove queue_models.hpp. Define the unchanged ListQueuesOptions alongside its paged response to keep header dependencies one-way and preserve layout. Update public includes, build list, and direct-header tests. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../azure-storage-queues/CMakeLists.txt | 1 - .../inc/azure/storage/queues.hpp | 1 - .../inc/azure/storage/queues/queue_models.hpp | 475 ---------------- .../azure/storage/queues/queue_options.hpp | 33 +- .../azure/storage/queues/queue_responses.hpp | 509 +++++++++++++++++- .../test/ut/queue_models_test.cpp | 7 +- 6 files changed, 507 insertions(+), 519 deletions(-) delete mode 100644 sdk/storage/azure-storage-queues/inc/azure/storage/queues/queue_models.hpp diff --git a/sdk/storage/azure-storage-queues/CMakeLists.txt b/sdk/storage/azure-storage-queues/CMakeLists.txt index 62c290ebe0..5d95b80280 100644 --- a/sdk/storage/azure-storage-queues/CMakeLists.txt +++ b/sdk/storage/azure-storage-queues/CMakeLists.txt @@ -44,7 +44,6 @@ set( inc/azure/storage/queues.hpp inc/azure/storage/queues/dll_import_export.hpp inc/azure/storage/queues/queue_client.hpp - inc/azure/storage/queues/queue_models.hpp inc/azure/storage/queues/queue_options.hpp inc/azure/storage/queues/queue_responses.hpp inc/azure/storage/queues/queue_sas_builder.hpp diff --git a/sdk/storage/azure-storage-queues/inc/azure/storage/queues.hpp b/sdk/storage/azure-storage-queues/inc/azure/storage/queues.hpp index 14b8bcdb6f..5771be1fdf 100644 --- a/sdk/storage/azure-storage-queues/inc/azure/storage/queues.hpp +++ b/sdk/storage/azure-storage-queues/inc/azure/storage/queues.hpp @@ -10,7 +10,6 @@ #include "azure/storage/queues/dll_import_export.hpp" #include "azure/storage/queues/queue_client.hpp" -#include "azure/storage/queues/queue_models.hpp" #include "azure/storage/queues/queue_options.hpp" #include "azure/storage/queues/queue_responses.hpp" #include "azure/storage/queues/queue_sas_builder.hpp" diff --git a/sdk/storage/azure-storage-queues/inc/azure/storage/queues/queue_models.hpp b/sdk/storage/azure-storage-queues/inc/azure/storage/queues/queue_models.hpp deleted file mode 100644 index a3c39bb9b6..0000000000 --- a/sdk/storage/azure-storage-queues/inc/azure/storage/queues/queue_models.hpp +++ /dev/null @@ -1,475 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -/** - * @file - * @brief Defines Queue service models and operation results. - */ - -#pragma once - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -namespace Azure { namespace Storage { namespace Queues { namespace Models { - /** - * @brief The retention policy. - */ - struct RetentionPolicy final - { - /** - * Indicates whether a retention policy is enabled for the storage service. - */ - bool IsEnabled = bool(); - /** - * Indicates the number of days that metrics or logging or soft-deleted data should be - * retained. All data older than this value will be deleted. - */ - Nullable Days; - }; - /** - * @brief Azure Analytics Logging settings. - */ - struct AnalyticsLogging final - { - /** - * The version of Storage Analytics to configure. - */ - std::string Version; - /** - * Indicates whether all delete requests should be logged. - */ - bool Delete = bool(); - /** - * Indicates whether all read requests should be logged. - */ - bool Read = bool(); - /** - * Indicates whether all write requests should be logged. - */ - bool Write = bool(); - /** - * The retention policy. - */ - Models::RetentionPolicy RetentionPolicy; - }; - /** - * @brief A summary of request statistics grouped by API in hour or minute aggregates for - * queues. - */ - struct Metrics final - { - /** - * The version of Storage Analytics to configure. - */ - std::string Version; - /** - * Indicates whether metrics are enabled for the Queue service. - */ - bool IsEnabled = bool(); - /** - * Indicates whether metrics should generate summary statistics for called API operations. - */ - Nullable IncludeApis; - /** - * The retention policy. - */ - Models::RetentionPolicy RetentionPolicy; - }; - /** - * @brief CORS is an HTTP feature that enables a web application running under one domain to - * access resources in another domain. Web browsers implement a security restriction known as - * same-origin policy that prevents a web page from calling APIs in a different domain; CORS - * provides a secure way to allow one domain (the origin domain) to call APIs in another domain. - */ - struct CorsRule final - { - /** - * The origin domains that are permitted to make a request against the storage service via - * CORS. The origin domain is the domain from which the request originates. Note that the - * origin must be an exact case-sensitive match with the origin that the user age sends to the - * service. You can also use the wildcard character '*' to allow all origin domains to make - * requests via CORS. - */ - std::string AllowedOrigins; - /** - * The methods (HTTP request verbs) that the origin domain may use for a CORS request. (comma - * separated). - */ - std::string AllowedMethods; - /** - * The request headers that the origin domain may specify on the CORS request. - */ - std::string AllowedHeaders; - /** - * The response headers that may be sent in the response to the CORS request and exposed by - * the browser to the request issuer. - */ - std::string ExposedHeaders; - /** - * The maximum amount time that a browser should cache the preflight OPTIONS request. - */ - std::int32_t MaxAgeInSeconds = std::int32_t(); - }; - /** - * @brief Response type for #Azure::Storage::Queues::QueueServiceClient::SetProperties. - */ - struct SetServicePropertiesResult final - { - }; - /** - * @brief Storage Service Properties. - */ - struct QueueServiceProperties final - { - /** - * Azure Analytics Logging settings. - */ - AnalyticsLogging Logging; - /** - * A summary of request statistics grouped by API in hourly aggregates for queues. - */ - Metrics HourMetrics; - /** - * A summary of request statistics grouped by API in minute aggregates for queues. - */ - Metrics MinuteMetrics; - /** - * The set of CORS rules. - */ - std::vector Cors; - }; - /** - * @brief The status of the secondary location. - */ - class GeoReplicationStatus final - : public Core::_internal::ExtendableEnumeration { - public: - /** Constructs a new GeoReplicationStatus instance */ - GeoReplicationStatus() = default; - /** Constructs a new GeoReplicationStatus from a string. */ - explicit GeoReplicationStatus(std::string value) : ExtendableEnumeration(std::move(value)) {} - - /** Constant value of type GeoReplicationStatus: Live */ - AZ_STORAGE_QUEUES_DLLEXPORT const static GeoReplicationStatus Live; - /** Constant value of type GeoReplicationStatus: Bootstrap */ - AZ_STORAGE_QUEUES_DLLEXPORT const static GeoReplicationStatus Bootstrap; - /** Constant value of type GeoReplicationStatus: Unavailable */ - AZ_STORAGE_QUEUES_DLLEXPORT const static GeoReplicationStatus Unavailable; - }; - /** - * @brief Geo-Replication information for the Secondary Storage Service. - */ - struct GeoReplication final - { - /** - * The status of the secondary location. - */ - GeoReplicationStatus Status; - /** - * A GMT date/time value, to the second. All primary writes preceding this value are - * guaranteed to be available for read operations at the secondary. Primary writes after this - * point in time may or may not be available for reads. - */ - Nullable LastSyncedOn; - }; - /** - * @brief Stats for the storage service. - */ - struct ServiceStatistics final - { - /** - * Geo-Replication information for the Secondary Storage Service. - */ - Models::GeoReplication GeoReplication; - }; - /** - * @brief A user delegation key. - */ - struct UserDelegationKey final - { - /** - * The Azure Active Directory object ID in GUID format. - */ - std::string SignedObjectId; - /** - * The Azure Active Directory tenant ID in GUID format. - */ - std::string SignedTenantId; - /** - * The date-time the key is active. - */ - DateTime SignedStartsOn; - /** - * The date-time the key expires. - */ - DateTime SignedExpiresOn; - /** - * Abbreviation of the Azure Storage service that accepts the key. - */ - std::string SignedService; - /** - * The service version that created the key. - */ - std::string SignedVersion; - /** - * The delegated user tenant id in Azure AD. Return if DelegatedUserTid is specified. - */ - Nullable SignedDelegatedUserTid; - /** - * The key as a base64 string. - */ - std::string Value; - }; - /** - * @brief An Azure Storage Queue. - */ - struct QueueItem final - { - /** - * The name of the Queue. - */ - std::string Name; - /** - * A set of name-value pairs associated with this queue. - */ - Core::CaseInsensitiveMap Metadata; - }; - /** - * @brief Include this parameter to specify that the queues' metadata be returned as part of the - * response body. - */ - enum class ListQueuesIncludeFlags - { - None = 0, - Metadata = 1, - }; - inline ListQueuesIncludeFlags operator|(ListQueuesIncludeFlags lhs, ListQueuesIncludeFlags rhs) - { - using type = std::underlying_type_t; - return static_cast(static_cast(lhs) | static_cast(rhs)); - } - inline ListQueuesIncludeFlags& operator|=(ListQueuesIncludeFlags& lhs, ListQueuesIncludeFlags rhs) - { - lhs = lhs | rhs; - return lhs; - } - inline ListQueuesIncludeFlags operator&(ListQueuesIncludeFlags lhs, ListQueuesIncludeFlags rhs) - { - using type = std::underlying_type_t; - return static_cast(static_cast(lhs) & static_cast(rhs)); - } - inline ListQueuesIncludeFlags& operator&=(ListQueuesIncludeFlags& lhs, ListQueuesIncludeFlags rhs) - { - lhs = lhs & rhs; - return lhs; - } - /** - * @brief Response type for #Azure::Storage::Queues::QueueClient::Create. - */ - struct CreateQueueResult final - { - /** - * Indicates if the queue was successfully created by this operation. - */ - bool Created = bool(); - }; - /** - * @brief Response type for #Azure::Storage::Queues::QueueClient::Delete. - */ - struct DeleteQueueResult final - { - /** - * Indicates if the queue was successfully deleted by this operation. - */ - bool Deleted = true; - }; - /** - * @brief Response type for #Azure::Storage::Queues::QueueClient::SetMetadata. - */ - struct SetQueueMetadataResult final - { - }; - /** - * @brief Signed identifier. - */ - struct SignedIdentifier final - { - /** - * A unique id. - */ - std::string Id; - /** - * The date-time the policy is active. - */ - Nullable StartsOn; - /** - * The date-time the policy expires. - */ - Nullable ExpiresOn; - /** - * The permissions for the acl policy. - */ - std::string Permissions; - }; - /** - * @brief Response type for #Azure::Storage::Queues::QueueClient::GetAccessPolicy. - */ - struct QueueAccessPolicy final - { - /** - * A collection of signed identifiers. - */ - std::vector SignedIdentifiers; - }; - /** - * @brief Response type for #Azure::Storage::Queues::QueueClient::SetAccessPolicy. - */ - struct SetQueueAccessPolicyResult final - { - }; - /** - * @brief The object returned in the QueueMessageList array when calling Get Messages on a - * Queue. - */ - struct QueueMessage final - { - /** - * The Id of the Message. - */ - std::string MessageId; - /** - * The time the Message was inserted into the Queue. - */ - DateTime InsertedOn; - /** - * The time that the Message will expire and be automatically deleted. - */ - DateTime ExpiresOn; - /** - * This value is required to delete the Message. If deletion fails using this popreceipt then - * the message has been dequeued by another client. - */ - std::string PopReceipt; - /** - * The time that the message will again become visible in the Queue. - */ - DateTime NextVisibleOn; - /** - * The number of times the message has been dequeued. - */ - std::int64_t DequeueCount = std::int64_t(); - /** - * The content of the Message. - */ - std::string MessageText; - }; - /** - * @brief Response type for #Azure::Storage::Queues::QueueClient::ReceiveMessages. - */ - struct ReceivedMessages final - { - /** - * The object returned when calling Get Messages on a Queue. - */ - std::vector Messages; - }; - /** - * @brief Response type for #Azure::Storage::Queues::QueueClient::ClearMessages. - */ - struct ClearMessagesResult final - { - }; - /** - * @brief The object returned in the QueueMessageList array when calling Put Message on a Queue. - */ - struct EnqueueMessageResult final - { - /** - * The Id of the Message. - */ - std::string MessageId; - /** - * The time the Message was inserted into the Queue. - */ - DateTime InsertedOn; - /** - * The time that the Message will expire and be automatically deleted. - */ - DateTime ExpiresOn; - /** - * This value is required to delete the Message. If deletion fails using this popreceipt then - * the message has been dequeued by another client. - */ - std::string PopReceipt; - /** - * The time that the message will again become visible in the Queue. - */ - DateTime NextVisibleOn; - }; - /** - * @brief The object returned in the QueueMessageList array when calling Peek Messages on a - * Queue. - */ - struct PeekedQueueMessage final - { - /** - * The Id of the Message. - */ - std::string MessageId; - /** - * The time the Message was inserted into the Queue. - */ - DateTime InsertedOn; - /** - * The time that the Message will expire and be automatically deleted. - */ - DateTime ExpiresOn; - /** - * The number of times the message has been dequeued. - */ - std::int64_t DequeueCount = std::int64_t(); - /** - * The content of the Message. - */ - std::string MessageText; - }; - /** - * @brief Response type for #Azure::Storage::Queues::QueueClient::PeekMessages. - */ - struct PeekedMessages final - { - /** - * The object returned when calling Peek Messages on a Queue. - */ - std::vector Messages; - }; - /** - * @brief Response type for #Azure::Storage::Queues::QueueClient::DeleteMessage. - */ - struct DeleteMessageResult final - { - }; - /** - * @brief Response type for #Azure::Storage::Queues::QueueClient::UpdateMessage. - */ - struct UpdateMessageResult final - { - /** - * The pop receipt of the queue message. - */ - std::string PopReceipt; - /** - * A UTC date/time value that represents when the message will be visible on the queue. - */ - DateTime NextVisibleOn; - }; -}}}} // namespace Azure::Storage::Queues::Models \ No newline at end of file diff --git a/sdk/storage/azure-storage-queues/inc/azure/storage/queues/queue_options.hpp b/sdk/storage/azure-storage-queues/inc/azure/storage/queues/queue_options.hpp index 7005780a56..dba7353b16 100644 --- a/sdk/storage/azure-storage-queues/inc/azure/storage/queues/queue_options.hpp +++ b/sdk/storage/azure-storage-queues/inc/azure/storage/queues/queue_options.hpp @@ -8,7 +8,7 @@ #pragma once -#include "azure/storage/queues/queue_models.hpp" +#include "azure/storage/queues/queue_responses.hpp" #include #include @@ -149,37 +149,6 @@ namespace Azure { namespace Storage { namespace Queues { Azure::Nullable Audience; }; - /** - * Optional parameters for #Azure::Storage::Queues::QueueServiceClient::ListQueues. - */ - struct ListQueuesOptions final - { - /** - * @brief Specifies a string that filters the results to return only queues whose name begins - * with the specified prefix. - */ - Azure::Nullable Prefix; - - /** - * @brief A string value that identifies the portion of the list of queues to be returned with - * the next listing operation. The operation returns a non-empty continuation token if the - * listing operation did not return all queues remaining to be listed with the current segment. - * The ContinuationToken value can be used as the value for the ContinuationToken parameter in a - * subsequent call to request the next segment of list items. - */ - Azure::Nullable ContinuationToken; - - /** - * @brief Specifies the maximum number of queues to return. - */ - Azure::Nullable PageSizeHint; - - /** - * @brief Specifies that the queue's metadata be returned. - */ - Models::ListQueuesIncludeFlags Include = Models::ListQueuesIncludeFlags::None; - }; - /** * Optional parameters for #Azure::Storage::Queues::QueueServiceClient::SetProperties. */ diff --git a/sdk/storage/azure-storage-queues/inc/azure/storage/queues/queue_responses.hpp b/sdk/storage/azure-storage-queues/inc/azure/storage/queues/queue_responses.hpp index ef893363af..bbc30623b9 100644 --- a/sdk/storage/azure-storage-queues/inc/azure/storage/queues/queue_responses.hpp +++ b/sdk/storage/azure-storage-queues/inc/azure/storage/queues/queue_responses.hpp @@ -2,25 +2,483 @@ // Licensed under the MIT License. /** * @file - * @brief Defines Queue operation responses. - * + * @brief Defines Queue service models and operation results. */ #pragma once -#include "azure/storage/queues/queue_models.hpp" -#include "azure/storage/queues/queue_options.hpp" - +#include +#include +#include +#include #include +#include +#include #include #include +#include +#include +#include namespace Azure { namespace Storage { namespace Queues { - - class QueueServiceClient; - namespace Models { + /** + * @brief The retention policy. + */ + struct RetentionPolicy final + { + /** + * Indicates whether a retention policy is enabled for the storage service. + */ + bool IsEnabled = bool(); + /** + * Indicates the number of days that metrics or logging or soft-deleted data should be + * retained. All data older than this value will be deleted. + */ + Nullable Days; + }; + /** + * @brief Azure Analytics Logging settings. + */ + struct AnalyticsLogging final + { + /** + * The version of Storage Analytics to configure. + */ + std::string Version; + /** + * Indicates whether all delete requests should be logged. + */ + bool Delete = bool(); + /** + * Indicates whether all read requests should be logged. + */ + bool Read = bool(); + /** + * Indicates whether all write requests should be logged. + */ + bool Write = bool(); + /** + * The retention policy. + */ + Models::RetentionPolicy RetentionPolicy; + }; + /** + * @brief A summary of request statistics grouped by API in hour or minute aggregates for + * queues. + */ + struct Metrics final + { + /** + * The version of Storage Analytics to configure. + */ + std::string Version; + /** + * Indicates whether metrics are enabled for the Queue service. + */ + bool IsEnabled = bool(); + /** + * Indicates whether metrics should generate summary statistics for called API operations. + */ + Nullable IncludeApis; + /** + * The retention policy. + */ + Models::RetentionPolicy RetentionPolicy; + }; + /** + * @brief CORS is an HTTP feature that enables a web application running under one domain to + * access resources in another domain. Web browsers implement a security restriction known as + * same-origin policy that prevents a web page from calling APIs in a different domain; CORS + * provides a secure way to allow one domain (the origin domain) to call APIs in another domain. + */ + struct CorsRule final + { + /** + * The origin domains that are permitted to make a request against the storage service via + * CORS. The origin domain is the domain from which the request originates. Note that the + * origin must be an exact case-sensitive match with the origin that the user age sends to the + * service. You can also use the wildcard character '*' to allow all origin domains to make + * requests via CORS. + */ + std::string AllowedOrigins; + /** + * The methods (HTTP request verbs) that the origin domain may use for a CORS request. (comma + * separated). + */ + std::string AllowedMethods; + /** + * The request headers that the origin domain may specify on the CORS request. + */ + std::string AllowedHeaders; + /** + * The response headers that may be sent in the response to the CORS request and exposed by + * the browser to the request issuer. + */ + std::string ExposedHeaders; + /** + * The maximum amount time that a browser should cache the preflight OPTIONS request. + */ + std::int32_t MaxAgeInSeconds = std::int32_t(); + }; + /** + * @brief Response type for #Azure::Storage::Queues::QueueServiceClient::SetProperties. + */ + struct SetServicePropertiesResult final + { + }; + /** + * @brief Storage Service Properties. + */ + struct QueueServiceProperties final + { + /** + * Azure Analytics Logging settings. + */ + AnalyticsLogging Logging; + /** + * A summary of request statistics grouped by API in hourly aggregates for queues. + */ + Metrics HourMetrics; + /** + * A summary of request statistics grouped by API in minute aggregates for queues. + */ + Metrics MinuteMetrics; + /** + * The set of CORS rules. + */ + std::vector Cors; + }; + /** + * @brief The status of the secondary location. + */ + class GeoReplicationStatus final + : public Core::_internal::ExtendableEnumeration { + public: + /** Constructs a new GeoReplicationStatus instance */ + GeoReplicationStatus() = default; + /** Constructs a new GeoReplicationStatus from a string. */ + explicit GeoReplicationStatus(std::string value) : ExtendableEnumeration(std::move(value)) {} + + /** Constant value of type GeoReplicationStatus: Live */ + AZ_STORAGE_QUEUES_DLLEXPORT const static GeoReplicationStatus Live; + /** Constant value of type GeoReplicationStatus: Bootstrap */ + AZ_STORAGE_QUEUES_DLLEXPORT const static GeoReplicationStatus Bootstrap; + /** Constant value of type GeoReplicationStatus: Unavailable */ + AZ_STORAGE_QUEUES_DLLEXPORT const static GeoReplicationStatus Unavailable; + }; + /** + * @brief Geo-Replication information for the Secondary Storage Service. + */ + struct GeoReplication final + { + /** + * The status of the secondary location. + */ + GeoReplicationStatus Status; + /** + * A GMT date/time value, to the second. All primary writes preceding this value are + * guaranteed to be available for read operations at the secondary. Primary writes after this + * point in time may or may not be available for reads. + */ + Nullable LastSyncedOn; + }; + /** + * @brief Stats for the storage service. + */ + struct ServiceStatistics final + { + /** + * Geo-Replication information for the Secondary Storage Service. + */ + Models::GeoReplication GeoReplication; + }; + /** + * @brief A user delegation key. + */ + struct UserDelegationKey final + { + /** + * The Azure Active Directory object ID in GUID format. + */ + std::string SignedObjectId; + /** + * The Azure Active Directory tenant ID in GUID format. + */ + std::string SignedTenantId; + /** + * The date-time the key is active. + */ + DateTime SignedStartsOn; + /** + * The date-time the key expires. + */ + DateTime SignedExpiresOn; + /** + * Abbreviation of the Azure Storage service that accepts the key. + */ + std::string SignedService; + /** + * The service version that created the key. + */ + std::string SignedVersion; + /** + * The delegated user tenant id in Azure AD. Return if DelegatedUserTid is specified. + */ + Nullable SignedDelegatedUserTid; + /** + * The key as a base64 string. + */ + std::string Value; + }; + /** + * @brief An Azure Storage Queue. + */ + struct QueueItem final + { + /** + * The name of the Queue. + */ + std::string Name; + /** + * A set of name-value pairs associated with this queue. + */ + Core::CaseInsensitiveMap Metadata; + }; + /** + * @brief Include this parameter to specify that the queues' metadata be returned as part of the + * response body. + */ + enum class ListQueuesIncludeFlags + { + None = 0, + Metadata = 1, + }; + inline ListQueuesIncludeFlags operator|(ListQueuesIncludeFlags lhs, ListQueuesIncludeFlags rhs) + { + using type = std::underlying_type_t; + return static_cast(static_cast(lhs) | static_cast(rhs)); + } + inline ListQueuesIncludeFlags& operator|=( + ListQueuesIncludeFlags& lhs, + ListQueuesIncludeFlags rhs) + { + lhs = lhs | rhs; + return lhs; + } + inline ListQueuesIncludeFlags operator&(ListQueuesIncludeFlags lhs, ListQueuesIncludeFlags rhs) + { + using type = std::underlying_type_t; + return static_cast(static_cast(lhs) & static_cast(rhs)); + } + inline ListQueuesIncludeFlags& operator&=( + ListQueuesIncludeFlags& lhs, + ListQueuesIncludeFlags rhs) + { + lhs = lhs & rhs; + return lhs; + } + /** + * @brief Response type for #Azure::Storage::Queues::QueueClient::Create. + */ + struct CreateQueueResult final + { + /** + * Indicates if the queue was successfully created by this operation. + */ + bool Created = bool(); + }; + /** + * @brief Response type for #Azure::Storage::Queues::QueueClient::Delete. + */ + struct DeleteQueueResult final + { + /** + * Indicates if the queue was successfully deleted by this operation. + */ + bool Deleted = true; + }; + /** + * @brief Response type for #Azure::Storage::Queues::QueueClient::SetMetadata. + */ + struct SetQueueMetadataResult final + { + }; + /** + * @brief Signed identifier. + */ + struct SignedIdentifier final + { + /** + * A unique id. + */ + std::string Id; + /** + * The date-time the policy is active. + */ + Nullable StartsOn; + /** + * The date-time the policy expires. + */ + Nullable ExpiresOn; + /** + * The permissions for the acl policy. + */ + std::string Permissions; + }; + /** + * @brief Response type for #Azure::Storage::Queues::QueueClient::GetAccessPolicy. + */ + struct QueueAccessPolicy final + { + /** + * A collection of signed identifiers. + */ + std::vector SignedIdentifiers; + }; + /** + * @brief Response type for #Azure::Storage::Queues::QueueClient::SetAccessPolicy. + */ + struct SetQueueAccessPolicyResult final + { + }; + /** + * @brief The object returned in the QueueMessageList array when calling Get Messages on a + * Queue. + */ + struct QueueMessage final + { + /** + * The Id of the Message. + */ + std::string MessageId; + /** + * The time the Message was inserted into the Queue. + */ + DateTime InsertedOn; + /** + * The time that the Message will expire and be automatically deleted. + */ + DateTime ExpiresOn; + /** + * This value is required to delete the Message. If deletion fails using this popreceipt then + * the message has been dequeued by another client. + */ + std::string PopReceipt; + /** + * The time that the message will again become visible in the Queue. + */ + DateTime NextVisibleOn; + /** + * The number of times the message has been dequeued. + */ + std::int64_t DequeueCount = std::int64_t(); + /** + * The content of the Message. + */ + std::string MessageText; + }; + /** + * @brief Response type for #Azure::Storage::Queues::QueueClient::ReceiveMessages. + */ + struct ReceivedMessages final + { + /** + * The object returned when calling Get Messages on a Queue. + */ + std::vector Messages; + }; + /** + * @brief Response type for #Azure::Storage::Queues::QueueClient::ClearMessages. + */ + struct ClearMessagesResult final + { + }; + /** + * @brief The object returned in the QueueMessageList array when calling Put Message on a Queue. + */ + struct EnqueueMessageResult final + { + /** + * The Id of the Message. + */ + std::string MessageId; + /** + * The time the Message was inserted into the Queue. + */ + DateTime InsertedOn; + /** + * The time that the Message will expire and be automatically deleted. + */ + DateTime ExpiresOn; + /** + * This value is required to delete the Message. If deletion fails using this popreceipt then + * the message has been dequeued by another client. + */ + std::string PopReceipt; + /** + * The time that the message will again become visible in the Queue. + */ + DateTime NextVisibleOn; + }; + /** + * @brief The object returned in the QueueMessageList array when calling Peek Messages on a + * Queue. + */ + struct PeekedQueueMessage final + { + /** + * The Id of the Message. + */ + std::string MessageId; + /** + * The time the Message was inserted into the Queue. + */ + DateTime InsertedOn; + /** + * The time that the Message will expire and be automatically deleted. + */ + DateTime ExpiresOn; + /** + * The number of times the message has been dequeued. + */ + std::int64_t DequeueCount = std::int64_t(); + /** + * The content of the Message. + */ + std::string MessageText; + }; + /** + * @brief Response type for #Azure::Storage::Queues::QueueClient::PeekMessages. + */ + struct PeekedMessages final + { + /** + * The object returned when calling Peek Messages on a Queue. + */ + std::vector Messages; + }; + /** + * @brief Response type for #Azure::Storage::Queues::QueueClient::DeleteMessage. + */ + struct DeleteMessageResult final + { + }; + /** + * @brief Response type for #Azure::Storage::Queues::QueueClient::UpdateMessage. + */ + struct UpdateMessageResult final + { + /** + * The pop receipt of the queue message. + */ + std::string PopReceipt; + /** + * A UTC date/time value that represents when the message will be visible on the queue. + */ + DateTime NextVisibleOn; + }; /** * @brief Response type for #Azure::Storage::Queues::QueueClient::GetProperties. */ @@ -46,6 +504,39 @@ namespace Azure { namespace Storage { namespace Queues { }; } // namespace Models + class QueueServiceClient; + + /** + * Optional parameters for #Azure::Storage::Queues::QueueServiceClient::ListQueues. + */ + struct ListQueuesOptions final + { + /** + * @brief Specifies a string that filters the results to return only queues whose name begins + * with the specified prefix. + */ + Azure::Nullable Prefix; + + /** + * @brief A string value that identifies the portion of the list of queues to be returned with + * the next listing operation. The operation returns a non-empty continuation token if the + * listing operation did not return all queues remaining to be listed with the current segment. + * The ContinuationToken value can be used as the value for the ContinuationToken parameter in a + * subsequent call to request the next segment of list items. + */ + Azure::Nullable ContinuationToken; + + /** + * @brief Specifies the maximum number of queues to return. + */ + Azure::Nullable PageSizeHint; + + /** + * @brief Specifies that the queue's metadata be returned. + */ + Models::ListQueuesIncludeFlags Include = Models::ListQueuesIncludeFlags::None; + }; + /** * @brief Response type for #Azure::Storage::Queues::QueueServiceClient::ListQueues. */ @@ -76,4 +567,4 @@ namespace Azure { namespace Storage { namespace Queues { friend class Azure::Core::PagedResponse; }; -}}} // namespace Azure::Storage::Queues +}}} // namespace Azure::Storage::Queues \ No newline at end of file diff --git a/sdk/storage/azure-storage-queues/test/ut/queue_models_test.cpp b/sdk/storage/azure-storage-queues/test/ut/queue_models_test.cpp index 3d12a4945d..e0eefc38dd 100644 --- a/sdk/storage/azure-storage-queues/test/ut/queue_models_test.cpp +++ b/sdk/storage/azure-storage-queues/test/ut/queue_models_test.cpp @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -#include +#include #include @@ -41,6 +41,11 @@ namespace Azure { namespace Storage { namespace Test { EXPECT_TRUE(Models::ReceivedMessages().Messages.empty()); EXPECT_EQ(Models::PeekedQueueMessage().DequeueCount, 0); EXPECT_TRUE(Models::PeekedMessages().Messages.empty()); + Queues::ListQueuesOptions list; + EXPECT_FALSE(list.Prefix.HasValue()); + EXPECT_FALSE(list.ContinuationToken.HasValue()); + EXPECT_FALSE(list.PageSizeHint.HasValue()); + EXPECT_EQ(list.Include, Models::ListQueuesIncludeFlags::None); } TEST(QueueModels, Enumerations) From ff75e88454959cba2b757ad7d213808c20fcfcb6 Mon Sep 17 00:00:00 2001 From: Jinming Hu Date: Sat, 24 Oct 2026 13:36:10 +1100 Subject: [PATCH 05/13] Keep Queue listing options and flags in options header Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../azure/storage/queues/queue_options.hpp | 73 ++++++++++++++++++- .../azure/storage/queues/queue_responses.hpp | 67 +---------------- 2 files changed, 74 insertions(+), 66 deletions(-) diff --git a/sdk/storage/azure-storage-queues/inc/azure/storage/queues/queue_options.hpp b/sdk/storage/azure-storage-queues/inc/azure/storage/queues/queue_options.hpp index dba7353b16..b391c7ad9c 100644 --- a/sdk/storage/azure-storage-queues/inc/azure/storage/queues/queue_options.hpp +++ b/sdk/storage/azure-storage-queues/inc/azure/storage/queues/queue_options.hpp @@ -8,17 +8,57 @@ #pragma once -#include "azure/storage/queues/queue_responses.hpp" +#include "azure/storage/queues/dll_import_export.hpp" +#include #include #include #include #include +#include #include +#include +#include namespace Azure { namespace Storage { namespace Queues { + namespace Models { + /** + * @brief Include this parameter to specify that the queues' metadata be returned as part of the + * response body. + */ + enum class ListQueuesIncludeFlags + { + None = 0, + Metadata = 1, + }; + inline ListQueuesIncludeFlags operator|(ListQueuesIncludeFlags lhs, ListQueuesIncludeFlags rhs) + { + using type = std::underlying_type_t; + return static_cast(static_cast(lhs) | static_cast(rhs)); + } + inline ListQueuesIncludeFlags& operator|=( + ListQueuesIncludeFlags& lhs, + ListQueuesIncludeFlags rhs) + { + lhs = lhs | rhs; + return lhs; + } + inline ListQueuesIncludeFlags operator&(ListQueuesIncludeFlags lhs, ListQueuesIncludeFlags rhs) + { + using type = std::underlying_type_t; + return static_cast(static_cast(lhs) & static_cast(rhs)); + } + inline ListQueuesIncludeFlags& operator&=( + ListQueuesIncludeFlags& lhs, + ListQueuesIncludeFlags rhs) + { + lhs = lhs & rhs; + return lhs; + } + } // namespace Models + namespace _detail { constexpr static const char* ApiVersion = "2026-04-06"; } // namespace _detail @@ -188,6 +228,37 @@ namespace Azure { namespace Storage { namespace Queues { Nullable DelegatedUserTid; }; + /** + * Optional parameters for #Azure::Storage::Queues::QueueServiceClient::ListQueues. + */ + struct ListQueuesOptions final + { + /** + * @brief Specifies a string that filters the results to return only queues whose name begins + * with the specified prefix. + */ + Azure::Nullable Prefix; + + /** + * @brief A string value that identifies the portion of the list of queues to be returned with + * the next listing operation. The operation returns a non-empty continuation token if the + * listing operation did not return all queues remaining to be listed with the current segment. + * The ContinuationToken value can be used as the value for the ContinuationToken parameter in a + * subsequent call to request the next segment of list items. + */ + Azure::Nullable ContinuationToken; + + /** + * @brief Specifies the maximum number of queues to return. + */ + Azure::Nullable PageSizeHint; + + /** + * @brief Specifies that the queue's metadata be returned. + */ + Models::ListQueuesIncludeFlags Include = Models::ListQueuesIncludeFlags::None; + }; + /** * Optional parameters for #Azure::Storage::Queues::QueueClient::Create. */ diff --git a/sdk/storage/azure-storage-queues/inc/azure/storage/queues/queue_responses.hpp b/sdk/storage/azure-storage-queues/inc/azure/storage/queues/queue_responses.hpp index bbc30623b9..2db4701f38 100644 --- a/sdk/storage/azure-storage-queues/inc/azure/storage/queues/queue_responses.hpp +++ b/sdk/storage/azure-storage-queues/inc/azure/storage/queues/queue_responses.hpp @@ -7,6 +7,8 @@ #pragma once +#include "azure/storage/queues/queue_options.hpp" + #include #include #include @@ -17,7 +19,6 @@ #include #include #include -#include #include #include @@ -246,39 +247,6 @@ namespace Azure { namespace Storage { namespace Queues { */ Core::CaseInsensitiveMap Metadata; }; - /** - * @brief Include this parameter to specify that the queues' metadata be returned as part of the - * response body. - */ - enum class ListQueuesIncludeFlags - { - None = 0, - Metadata = 1, - }; - inline ListQueuesIncludeFlags operator|(ListQueuesIncludeFlags lhs, ListQueuesIncludeFlags rhs) - { - using type = std::underlying_type_t; - return static_cast(static_cast(lhs) | static_cast(rhs)); - } - inline ListQueuesIncludeFlags& operator|=( - ListQueuesIncludeFlags& lhs, - ListQueuesIncludeFlags rhs) - { - lhs = lhs | rhs; - return lhs; - } - inline ListQueuesIncludeFlags operator&(ListQueuesIncludeFlags lhs, ListQueuesIncludeFlags rhs) - { - using type = std::underlying_type_t; - return static_cast(static_cast(lhs) & static_cast(rhs)); - } - inline ListQueuesIncludeFlags& operator&=( - ListQueuesIncludeFlags& lhs, - ListQueuesIncludeFlags rhs) - { - lhs = lhs & rhs; - return lhs; - } /** * @brief Response type for #Azure::Storage::Queues::QueueClient::Create. */ @@ -506,37 +474,6 @@ namespace Azure { namespace Storage { namespace Queues { class QueueServiceClient; - /** - * Optional parameters for #Azure::Storage::Queues::QueueServiceClient::ListQueues. - */ - struct ListQueuesOptions final - { - /** - * @brief Specifies a string that filters the results to return only queues whose name begins - * with the specified prefix. - */ - Azure::Nullable Prefix; - - /** - * @brief A string value that identifies the portion of the list of queues to be returned with - * the next listing operation. The operation returns a non-empty continuation token if the - * listing operation did not return all queues remaining to be listed with the current segment. - * The ContinuationToken value can be used as the value for the ContinuationToken parameter in a - * subsequent call to request the next segment of list items. - */ - Azure::Nullable ContinuationToken; - - /** - * @brief Specifies the maximum number of queues to return. - */ - Azure::Nullable PageSizeHint; - - /** - * @brief Specifies that the queue's metadata be returned. - */ - Models::ListQueuesIncludeFlags Include = Models::ListQueuesIncludeFlags::None; - }; - /** * @brief Response type for #Azure::Storage::Queues::QueueServiceClient::ListQueues. */ From 7ee29dd841b8cfb30ff551d15182619fd99642ce Mon Sep 17 00:00:00 2001 From: Jinming Hu Date: Sat, 24 Oct 2026 13:41:18 +1100 Subject: [PATCH 06/13] Use explicit false defaults in handwritten Queue models Add the missing terminal newline to queue_responses.hpp. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../inc/azure/storage/queues/queue_responses.hpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sdk/storage/azure-storage-queues/inc/azure/storage/queues/queue_responses.hpp b/sdk/storage/azure-storage-queues/inc/azure/storage/queues/queue_responses.hpp index 2db4701f38..0a925ff409 100644 --- a/sdk/storage/azure-storage-queues/inc/azure/storage/queues/queue_responses.hpp +++ b/sdk/storage/azure-storage-queues/inc/azure/storage/queues/queue_responses.hpp @@ -32,7 +32,7 @@ namespace Azure { namespace Storage { namespace Queues { /** * Indicates whether a retention policy is enabled for the storage service. */ - bool IsEnabled = bool(); + bool IsEnabled = false; /** * Indicates the number of days that metrics or logging or soft-deleted data should be * retained. All data older than this value will be deleted. @@ -51,15 +51,15 @@ namespace Azure { namespace Storage { namespace Queues { /** * Indicates whether all delete requests should be logged. */ - bool Delete = bool(); + bool Delete = false; /** * Indicates whether all read requests should be logged. */ - bool Read = bool(); + bool Read = false; /** * Indicates whether all write requests should be logged. */ - bool Write = bool(); + bool Write = false; /** * The retention policy. */ @@ -78,7 +78,7 @@ namespace Azure { namespace Storage { namespace Queues { /** * Indicates whether metrics are enabled for the Queue service. */ - bool IsEnabled = bool(); + bool IsEnabled = false; /** * Indicates whether metrics should generate summary statistics for called API operations. */ @@ -255,7 +255,7 @@ namespace Azure { namespace Storage { namespace Queues { /** * Indicates if the queue was successfully created by this operation. */ - bool Created = bool(); + bool Created = false; }; /** * @brief Response type for #Azure::Storage::Queues::QueueClient::Delete. @@ -504,4 +504,4 @@ namespace Azure { namespace Storage { namespace Queues { friend class Azure::Core::PagedResponse; }; -}}} // namespace Azure::Storage::Queues \ No newline at end of file +}}} // namespace Azure::Storage::Queues From 79fa18c146ea48a53de123ee973d5d3493a66501 Mon Sep 17 00:00:00 2001 From: Jinming Hu Date: Sat, 24 Oct 2026 14:18:02 +1100 Subject: [PATCH 07/13] Remove Queue protocol header normalization policy Use the standard Storage pipeline and retain generated XML headers. Update offline protocol assertions for the intended headers. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../src/private/queue_protocol.hpp | 41 ------------------- .../azure-storage-queues/src/queue_client.cpp | 6 +-- .../src/queue_service_client.cpp | 6 +-- .../test/ut/queue_protocol_test.cpp | 26 ++++++++++-- 4 files changed, 29 insertions(+), 50 deletions(-) diff --git a/sdk/storage/azure-storage-queues/src/private/queue_protocol.hpp b/sdk/storage/azure-storage-queues/src/private/queue_protocol.hpp index 15125418c9..4c91d136d8 100644 --- a/sdk/storage/azure-storage-queues/src/private/queue_protocol.hpp +++ b/sdk/storage/azure-storage-queues/src/private/queue_protocol.hpp @@ -4,53 +4,12 @@ #pragma once #include -#include -#include #include #include namespace Azure { namespace Storage { namespace Queues { namespace _detail { - class QueueProtocolPolicy final : public Core::Http::Policies::HttpPolicy { - public: - std::unique_ptr Clone() const override - { - return std::make_unique(); - } - - std::unique_ptr Send( - Core::Http::Request& request, - Core::Http::Policies::NextHttpPolicy nextPolicy, - Core::Context const& context) const override - { - // The previous protocol layer did not set Accept. User policies can still set it. - request.RemoveHeader("Accept"); - auto const contentType = request.GetHeader("Content-Type"); - if (contentType.HasValue() && contentType.Value() == "application/xml") - { - if (request.GetBodyStream()->Length() == 0) - { - request.RemoveHeader("Content-Type"); - } - else - { - request.SetHeader("Content-Type", "application/xml; charset=UTF-8"); - } - } - return nextPolicy.Send(request, context); - } - }; - - inline std::vector> BuildQueuePipelinePolicies( - Core::_internal::ClientOptions const& options, - _internal::BuildStoragePipelineOptions pipelineOptions) - { - auto policies = _internal::BuildHttpPipelinePolicies(options, std::move(pipelineOptions)); - policies.insert(policies.begin(), std::make_unique()); - return policies; - } - template auto InvokeStorageProtocol(Operation&& operation) -> decltype(std::forward(operation)()) diff --git a/sdk/storage/azure-storage-queues/src/queue_client.cpp b/sdk/storage/azure-storage-queues/src/queue_client.cpp index 930282ba7d..dbf56214cc 100644 --- a/sdk/storage/azure-storage-queues/src/queue_client.cpp +++ b/sdk/storage/azure-storage-queues/src/queue_client.cpp @@ -53,7 +53,7 @@ namespace Azure { namespace Storage { namespace Queues { pipelineOptions.SharedKeyAuthPolicy = std::make_unique<_internal::SharedKeyPolicy>(credential); m_pipeline = std::make_shared( - _detail::BuildQueuePipelinePolicies(options, std::move(pipelineOptions))); + _internal::BuildHttpPipelinePolicies(options, std::move(pipelineOptions))); } QueueClient::QueueClient( @@ -80,7 +80,7 @@ namespace Azure { namespace Storage { namespace Queues { } m_pipeline = std::make_shared( - _detail::BuildQueuePipelinePolicies(options, std::move(pipelineOptions))); + _internal::BuildHttpPipelinePolicies(options, std::move(pipelineOptions))); } QueueClient::QueueClient(const std::string& queueUrl, const QueueClientOptions& options) @@ -94,7 +94,7 @@ namespace Azure { namespace Storage { namespace Queues { pipelineOptions.ApiVersion = options.ApiVersion.ToString(); m_pipeline = std::make_shared( - _detail::BuildQueuePipelinePolicies(options, std::move(pipelineOptions))); + _internal::BuildHttpPipelinePolicies(options, std::move(pipelineOptions))); } Azure::Response QueueClient::Create( diff --git a/sdk/storage/azure-storage-queues/src/queue_service_client.cpp b/sdk/storage/azure-storage-queues/src/queue_service_client.cpp index 17ecfe18dc..37748ba60f 100644 --- a/sdk/storage/azure-storage-queues/src/queue_service_client.cpp +++ b/sdk/storage/azure-storage-queues/src/queue_service_client.cpp @@ -50,7 +50,7 @@ namespace Azure { namespace Storage { namespace Queues { pipelineOptions.SharedKeyAuthPolicy = std::make_unique<_internal::SharedKeyPolicy>(credential); m_pipeline = std::make_shared( - _detail::BuildQueuePipelinePolicies(options, std::move(pipelineOptions))); + _internal::BuildHttpPipelinePolicies(options, std::move(pipelineOptions))); } QueueServiceClient::QueueServiceClient( @@ -77,7 +77,7 @@ namespace Azure { namespace Storage { namespace Queues { } m_pipeline = std::make_shared( - _detail::BuildQueuePipelinePolicies(options, std::move(pipelineOptions))); + _internal::BuildHttpPipelinePolicies(options, std::move(pipelineOptions))); } QueueServiceClient::QueueServiceClient( @@ -93,7 +93,7 @@ namespace Azure { namespace Storage { namespace Queues { pipelineOptions.ApiVersion = options.ApiVersion.ToString(); m_pipeline = std::make_shared( - _detail::BuildQueuePipelinePolicies(options, std::move(pipelineOptions))); + _internal::BuildHttpPipelinePolicies(options, std::move(pipelineOptions))); } QueueClient QueueServiceClient::GetQueueClient(const std::string& queueName) const diff --git a/sdk/storage/azure-storage-queues/test/ut/queue_protocol_test.cpp b/sdk/storage/azure-storage-queues/test/ut/queue_protocol_test.cpp index c41bf5b535..391b7138ec 100644 --- a/sdk/storage/azure-storage-queues/test/ut/queue_protocol_test.cpp +++ b/sdk/storage/azure-storage-queues/test/ut/queue_protocol_test.cpp @@ -464,7 +464,22 @@ namespace Azure { namespace Storage { namespace Test { namespace { } EXPECT_NE(Transport->Requests[4].Body.find("MessageText"), std::string::npos); EXPECT_NE(Transport->Requests[5].Body.find("a&b"), std::string::npos); - EXPECT_EQ(Transport->Requests[2].Headers.at("Content-Type"), "application/xml; charset=UTF-8"); + for (auto const index : {0, 1, 2}) + { + EXPECT_EQ(Transport->Requests[index].Headers.at("Accept"), "application/xml"); + } + for (auto const index : {2, 4, 5}) + { + EXPECT_EQ(Transport->Requests[index].Headers.at("Content-Type"), "application/xml"); + } + for (auto const index : {0, 1, 6, 7}) + { + EXPECT_EQ(Transport->Requests[index].Headers.count("Content-Type"), 0U); + } + for (auto const index : {3, 4, 5, 6, 7}) + { + EXPECT_EQ(Transport->Requests[index].Headers.count("Accept"), 0U); + } EXPECT_EQ(Transport->Requests[6].Url.GetQueryParameters().at("popreceipt"), "receipt"); for (auto const& request : Transport->Requests) { @@ -578,8 +593,13 @@ namespace Azure { namespace Storage { namespace Test { namespace { EXPECT_FALSE(client.GetStatistics().Value.GeoReplication.LastSyncedOn.HasValue()); ASSERT_EQ(Transport->Requests.size(), 4U); EXPECT_NE(Transport->Requests[0].Body.find(""), std::string::npos); - EXPECT_EQ(Transport->Requests[0].Headers.at("Content-Type"), "application/xml; charset=UTF-8"); - EXPECT_EQ(Transport->Requests[1].Headers.count("Accept"), 0U); + EXPECT_EQ(Transport->Requests[0].Headers.at("Content-Type"), "application/xml"); + EXPECT_EQ(Transport->Requests[0].Headers.count("Accept"), 0U); + for (auto const index : {1, 2, 3}) + { + EXPECT_EQ(Transport->Requests[index].Headers.at("Accept"), "application/xml"); + EXPECT_EQ(Transport->Requests[index].Headers.count("Content-Type"), 0U); + } EXPECT_EQ(Transport->Requests[0].Body.find(""), std::string::npos); EXPECT_EQ(Transport->Requests[0].Url.GetQueryParameters().at("restype"), "service"); EXPECT_TRUE(Transport->Requests[1].HasReplicaStatus); From abfa405b89a0be20f701db2949939c462ed56efd Mon Sep 17 00:00:00 2001 From: Jinming Hu Date: Sat, 24 Oct 2026 14:50:43 +1100 Subject: [PATCH 08/13] Share Storage protocol exception conversion in Common Move the unchanged invocation helper to an installed Common internal header, add generic tests, and require the existing next Common version from Queue. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../azure-storage-common/CMakeLists.txt | 1 + .../common/internal/storage_protocol.hpp} | 4 +- .../test/ut/CMakeLists.txt | 1 + .../test/ut/storage_protocol_test.cpp | 149 ++++++++++++++++++ .../azure-storage-queues/CMakeLists.txt | 5 +- .../azure-storage-queues/src/queue_client.cpp | 26 +-- .../src/queue_service_client.cpp | 12 +- .../test/ut/queue_protocol_test.cpp | 45 ------ sdk/storage/azure-storage-queues/vcpkg.json | 5 +- .../vcpkg/Config.cmake.in | 2 +- .../azure-storage-queues/vcpkg/vcpkg.json | 2 +- 11 files changed, 180 insertions(+), 72 deletions(-) rename sdk/storage/{azure-storage-queues/src/private/queue_protocol.hpp => azure-storage-common/inc/azure/storage/common/internal/storage_protocol.hpp} (84%) create mode 100644 sdk/storage/azure-storage-common/test/ut/storage_protocol_test.cpp diff --git a/sdk/storage/azure-storage-common/CMakeLists.txt b/sdk/storage/azure-storage-common/CMakeLists.txt index d2e3d724e6..15fca9e297 100644 --- a/sdk/storage/azure-storage-common/CMakeLists.txt +++ b/sdk/storage/azure-storage-common/CMakeLists.txt @@ -57,6 +57,7 @@ set( inc/azure/storage/common/internal/storage_data_locality_policy.hpp inc/azure/storage/common/internal/storage_per_retry_policy.hpp inc/azure/storage/common/internal/storage_pipeline.hpp + inc/azure/storage/common/internal/storage_protocol.hpp inc/azure/storage/common/internal/storage_retry_policy.hpp inc/azure/storage/common/internal/storage_service_version_policy.hpp inc/azure/storage/common/internal/storage_switch_to_secondary_policy.hpp diff --git a/sdk/storage/azure-storage-queues/src/private/queue_protocol.hpp b/sdk/storage/azure-storage-common/inc/azure/storage/common/internal/storage_protocol.hpp similarity index 84% rename from sdk/storage/azure-storage-queues/src/private/queue_protocol.hpp rename to sdk/storage/azure-storage-common/inc/azure/storage/common/internal/storage_protocol.hpp index 4c91d136d8..d82114aa84 100644 --- a/sdk/storage/azure-storage-queues/src/private/queue_protocol.hpp +++ b/sdk/storage/azure-storage-common/inc/azure/storage/common/internal/storage_protocol.hpp @@ -8,7 +8,7 @@ #include -namespace Azure { namespace Storage { namespace Queues { namespace _detail { +namespace Azure { namespace Storage { namespace _internal { template auto InvokeStorageProtocol(Operation&& operation) @@ -32,4 +32,4 @@ namespace Azure { namespace Storage { namespace Queues { namespace _detail { } } -}}}} // namespace Azure::Storage::Queues::_detail +}}} // namespace Azure::Storage::_internal diff --git a/sdk/storage/azure-storage-common/test/ut/CMakeLists.txt b/sdk/storage/azure-storage-common/test/ut/CMakeLists.txt index ccce4f50f9..d6116077b3 100644 --- a/sdk/storage/azure-storage-common/test/ut/CMakeLists.txt +++ b/sdk/storage/azure-storage-common/test/ut/CMakeLists.txt @@ -19,6 +19,7 @@ add_executable ( crypt_functions_test.cpp metadata_test.cpp storage_credential_test.cpp + storage_protocol_test.cpp storage_url_test.cpp structured_message_test.cpp test_base.cpp diff --git a/sdk/storage/azure-storage-common/test/ut/storage_protocol_test.cpp b/sdk/storage/azure-storage-common/test/ut/storage_protocol_test.cpp new file mode 100644 index 0000000000..dad682a472 --- /dev/null +++ b/sdk/storage/azure-storage-common/test/ut/storage_protocol_test.cpp @@ -0,0 +1,149 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include + +#include +#include +#include + +#include + +namespace Azure { namespace Storage { namespace Test { + namespace Http = Core::Http; + + TEST(StorageProtocol, PreservesReturnTypesAndForwarding) + { + bool invoked = false; + _internal::InvokeStorageProtocol([&] { invoked = true; }); + EXPECT_TRUE(invoked); + EXPECT_EQ(_internal::InvokeStorageProtocol([] { return 42; }), 42); + auto value = _internal::InvokeStorageProtocol([] { return std::make_unique(42); }); + EXPECT_EQ(*value, 42); + + int number = 7; + auto reference = [&]() -> int& { return number; }; + static_assert( + std::is_same::value, + "Preserve reference return types"); + EXPECT_EQ(&_internal::InvokeStorageProtocol(reference), &number); + auto rvalueReference = [&]() -> int&& { return std::move(number); }; + static_assert( + std::is_same::value, + "Preserve rvalue reference return types"); + + struct MoveOnlyOperation + { + std::unique_ptr Value; + explicit MoveOnlyOperation(int value) : Value(std::make_unique(value)) {} + int operator()() && { return *Value; } + int operator()() & = delete; + }; + EXPECT_EQ(_internal::InvokeStorageProtocol(MoveOnlyOperation(9)), 9); + } + + TEST(StorageProtocol, PreservesExistingExceptions) + { + StorageException const* existing = nullptr; + try + { + _internal::InvokeStorageProtocol([&] { + try + { + StorageException error("existing Storage exception"); + error.ErrorCode = "Existing"; + error.AdditionalInformation["Detail"] = "retained"; + error.RawResponse = std::make_unique( + 1, 1, Http::HttpStatusCode::Conflict, "Conflict"); + throw error; + } + catch (StorageException const& error) + { + existing = &error; + throw; + } + }); + FAIL() << "Expected StorageException"; + } + catch (StorageException const& error) + { + EXPECT_EQ(&error, existing); + EXPECT_STREQ(error.what(), "existing Storage exception"); + EXPECT_EQ(error.ErrorCode, "Existing"); + EXPECT_EQ(error.AdditionalInformation.at("Detail"), "retained"); + ASSERT_NE(error.RawResponse, nullptr); + } + Core::RequestFailedException const* withoutResponse = nullptr; + try + { + _internal::InvokeStorageProtocol([&] { + try + { + throw Core::RequestFailedException("response parsing failed"); + } + catch (Core::RequestFailedException const& error) + { + withoutResponse = &error; + throw; + } + }); + FAIL() << "Expected Core::RequestFailedException"; + } + catch (StorageException const&) + { + FAIL() << "An exception without an HTTP response must not be translated"; + } + catch (Core::RequestFailedException const& error) + { + EXPECT_EQ(&error, withoutResponse); + EXPECT_STREQ(error.what(), "response parsing failed"); + EXPECT_EQ(error.RawResponse, nullptr); + } + EXPECT_THROW( + _internal::InvokeStorageProtocol([] { throw std::logic_error("unrelated"); }), + std::logic_error); + } + + TEST(StorageProtocol, ConvertsResponseAndTransfersOwnership) + { + auto response = std::make_unique( + 1, 1, Http::HttpStatusCode::BadRequest, "Bad Request"); + response->SetHeader("content-type", "application/xml"); + response->SetHeader("x-ms-request-id", "request-id"); + response->SetHeader("x-ms-client-request-id", "client-id"); + std::string const body = "InvalidMetadataInvalid value" + "retained"; + response->SetBody(std::vector(body.begin(), body.end())); + Http::RawResponse const* originalResponse = nullptr; + try + { + _internal::InvokeStorageProtocol([&] { + try + { + Core::RequestFailedException error("generated failure"); + error.RawResponse = std::move(response); + throw error; + } + catch (Core::RequestFailedException const& error) + { + originalResponse = error.RawResponse.get(); + throw; + } + }); + FAIL() << "Expected StorageException"; + } + catch (StorageException const& error) + { + EXPECT_EQ(error.RawResponse.get(), originalResponse); + EXPECT_EQ(error.StatusCode, Http::HttpStatusCode::BadRequest); + EXPECT_EQ(error.ErrorCode, "InvalidMetadata"); + EXPECT_EQ(error.Message, "Invalid value"); + EXPECT_EQ(error.RequestId, "request-id"); + EXPECT_EQ(error.ClientRequestId, "client-id"); + EXPECT_EQ(error.AdditionalInformation.at("Detail"), "retained"); + ASSERT_NE(error.RawResponse, nullptr); + EXPECT_EQ(error.RawResponse->GetHeaders().at("content-type"), "application/xml"); + } + EXPECT_EQ(response, nullptr); + } +}}} // namespace Azure::Storage::Test diff --git a/sdk/storage/azure-storage-queues/CMakeLists.txt b/sdk/storage/azure-storage-queues/CMakeLists.txt index 5d95b80280..6e73185e15 100644 --- a/sdk/storage/azure-storage-queues/CMakeLists.txt +++ b/sdk/storage/azure-storage-queues/CMakeLists.txt @@ -33,9 +33,9 @@ if(FETCH_SOURCE_DEPS) add_subdirectory(${oneFolder} EXCLUDE_FROM_ALL) endforeach() elseif(NOT AZ_ALL_LIBRARIES) - find_package(azure-storage-common-cpp CONFIG QUIET) + find_package(azure-storage-common-cpp 12.16.0 CONFIG QUIET) if(NOT azure-storage-common-cpp_FOUND) - find_package(azure-storage-common-cpp REQUIRED) + find_package(azure-storage-common-cpp 12.16.0 REQUIRED) endif() endif() @@ -55,7 +55,6 @@ set( AZURE_STORAGE_QUEUES_SOURCE src/private/package_version.hpp src/private/queue_model_conversions.hpp - src/private/queue_protocol.hpp src/generated/queues_client.hpp src/generated/queues_client_options.hpp src/generated/queues_models.hpp diff --git a/sdk/storage/azure-storage-queues/src/queue_client.cpp b/sdk/storage/azure-storage-queues/src/queue_client.cpp index dbf56214cc..5437b122c4 100644 --- a/sdk/storage/azure-storage-queues/src/queue_client.cpp +++ b/sdk/storage/azure-storage-queues/src/queue_client.cpp @@ -5,13 +5,13 @@ #include "private/package_version.hpp" #include "private/queue_model_conversions.hpp" -#include "private/queue_protocol.hpp" #include #include #include #include #include +#include #include #include @@ -105,7 +105,7 @@ namespace Azure { namespace Storage { namespace Queues { { _detail::QueuesClientCreateQueueOptions protocolLayerOptions; protocolLayerOptions.Metadata = options.Metadata; - auto response = _detail::InvokeStorageProtocol([&] { + auto response = _internal::InvokeStorageProtocol([&] { return _detail::QueuesClient(m_queueUrl, m_pipeline) .CreateQueue(protocolLayerOptions, context); }); @@ -133,7 +133,7 @@ namespace Azure { namespace Storage { namespace Queues { (void)options; try { - auto response = _detail::InvokeStorageProtocol( + auto response = _internal::InvokeStorageProtocol( [&] { return _detail::QueuesClient(m_queueUrl, m_pipeline).DeleteQueue({}, context); }); return Azure::Response({}, std::move(response.RawResponse)); } @@ -154,7 +154,7 @@ namespace Azure { namespace Storage { namespace Queues { const Azure::Core::Context& context) const { (void)options; - auto ret = _detail::InvokeStorageProtocol([&] { + auto ret = _internal::InvokeStorageProtocol([&] { return _detail::QueuesClient(m_queueUrl, m_pipeline).GetPropertiesQueue({}, context); }); Models::QueueProperties queueProperties; @@ -188,7 +188,7 @@ namespace Azure { namespace Storage { namespace Queues { (void)options; _detail::QueuesClientSetMetadataQueueOptions protocolLayerOptions; protocolLayerOptions.Metadata = std::move(metadata); - auto response = _detail::InvokeStorageProtocol([&] { + auto response = _internal::InvokeStorageProtocol([&] { return _detail::QueuesClient(m_queueUrl, m_pipeline) .SetMetadataQueue(protocolLayerOptions, context); }); @@ -200,7 +200,7 @@ namespace Azure { namespace Storage { namespace Queues { const Azure::Core::Context& context) const { (void)options; - auto response = _detail::InvokeStorageProtocol([&] { + auto response = _internal::InvokeStorageProtocol([&] { return _detail::QueuesClient(m_queueUrl, m_pipeline).GetAccessPolicyQueue({}, context); }); return Azure::Response( @@ -215,7 +215,7 @@ namespace Azure { namespace Storage { namespace Queues { (void)options; _detail::QueuesClientSetAccessPolicyQueueOptions protocolLayerOptions; protocolLayerOptions.QueueAcl = _detail::ToGenerated(accessPolicy); - auto response = _detail::InvokeStorageProtocol([&] { + auto response = _internal::InvokeStorageProtocol([&] { return _detail::QueuesClient(m_queueUrl, m_pipeline) .SetAccessPolicyQueue(protocolLayerOptions, context); }); @@ -239,7 +239,7 @@ namespace Azure { namespace Storage { namespace Queues { protocolLayerOptions.Visibilitytimeout = static_cast(options.VisibilityTimeout.Value().count()); } - auto response = _detail::InvokeStorageProtocol([&] { + auto response = _internal::InvokeStorageProtocol([&] { return _detail::QueuesClient(m_queueUrl, m_pipeline) .SendMessageQueue(message, protocolLayerOptions, context); }); @@ -268,7 +268,7 @@ namespace Azure { namespace Storage { namespace Queues { protocolLayerOptions.Visibilitytimeout = static_cast(options.VisibilityTimeout.Value().count()); } - auto response = _detail::InvokeStorageProtocol([&] { + auto response = _internal::InvokeStorageProtocol([&] { return _detail::QueuesClient(m_queueUrl, m_pipeline) .ReceiveMessagesQueue(protocolLayerOptions, context); }); @@ -288,7 +288,7 @@ namespace Azure { namespace Storage { namespace Queues { { _detail::QueuesClientPeekMessagesQueueOptions protocolLayerOptions; protocolLayerOptions.Numofmessages = options.MaxMessages; - auto response = _detail::InvokeStorageProtocol([&] { + auto response = _internal::InvokeStorageProtocol([&] { return _detail::QueuesClient(m_queueUrl, m_pipeline) .PeekMessagesQueue(protocolLayerOptions, context); }); @@ -316,7 +316,7 @@ namespace Azure { namespace Storage { namespace Queues { message.MessageText = options.MessageText.Value(); protocolLayerOptions.QueueMessage = std::move(message); } - auto response = _detail::InvokeStorageProtocol([&] { + auto response = _internal::InvokeStorageProtocol([&] { return _detail::QueuesClient(m_queueUrl, m_pipeline) .UpdateMessageQueue( messageId, @@ -339,7 +339,7 @@ namespace Azure { namespace Storage { namespace Queues { const Azure::Core::Context& context) const { (void)options; - auto response = _detail::InvokeStorageProtocol([&] { + auto response = _internal::InvokeStorageProtocol([&] { return _detail::QueuesClient(m_queueUrl, m_pipeline) .DeleteMessageQueue(messageId, popReceipt, {}, context); }); @@ -351,7 +351,7 @@ namespace Azure { namespace Storage { namespace Queues { const Azure::Core::Context& context) const { (void)options; - auto response = _detail::InvokeStorageProtocol( + auto response = _internal::InvokeStorageProtocol( [&] { return _detail::QueuesClient(m_queueUrl, m_pipeline).ClearQueue({}, context); }); return Azure::Response({}, std::move(response.RawResponse)); } diff --git a/sdk/storage/azure-storage-queues/src/queue_service_client.cpp b/sdk/storage/azure-storage-queues/src/queue_service_client.cpp index 37748ba60f..bf0339785c 100644 --- a/sdk/storage/azure-storage-queues/src/queue_service_client.cpp +++ b/sdk/storage/azure-storage-queues/src/queue_service_client.cpp @@ -5,13 +5,13 @@ #include "private/package_version.hpp" #include "private/queue_model_conversions.hpp" -#include "private/queue_protocol.hpp" #include #include #include #include #include +#include #include #include @@ -117,7 +117,7 @@ namespace Azure { namespace Storage { namespace Queues { protocolLayerOptions.Include = std::vector<_detail::Models::ListQueuesIncludeType>{ _detail::Models::ListQueuesIncludeType::Metadata}; } - auto response = _detail::InvokeStorageProtocol([&] { + auto response = _internal::InvokeStorageProtocol([&] { return _detail::QueuesClient(m_serviceUrl, m_pipeline) .GetQueuesService(protocolLayerOptions, _internal::WithReplicaStatus(context)); }); @@ -160,7 +160,7 @@ namespace Azure { namespace Storage { namespace Queues { { (void)options; auto protocolProperties = _detail::ToGenerated(std::move(properties)); - auto response = _detail::InvokeStorageProtocol([&] { + auto response = _internal::InvokeStorageProtocol([&] { return _detail::QueuesClient(m_serviceUrl, m_pipeline) .SetPropertiesService(protocolProperties, {}, context); }); @@ -172,7 +172,7 @@ namespace Azure { namespace Storage { namespace Queues { const Azure::Core::Context& context) const { (void)options; - auto response = _detail::InvokeStorageProtocol([&] { + auto response = _internal::InvokeStorageProtocol([&] { return _detail::QueuesClient(m_serviceUrl, m_pipeline) .GetPropertiesService({}, _internal::WithReplicaStatus(context)); }); @@ -185,7 +185,7 @@ namespace Azure { namespace Storage { namespace Queues { const Azure::Core::Context& context) const { (void)options; - auto response = _detail::InvokeStorageProtocol([&] { + auto response = _internal::InvokeStorageProtocol([&] { return _detail::QueuesClient(m_serviceUrl, m_pipeline).GetStatisticsService({}, context); }); Models::ServiceStatistics result; @@ -229,7 +229,7 @@ namespace Azure { namespace Storage { namespace Queues { keyInfo.Expiry = expiresOn.ToString(DateTime::DateFormat::Rfc3339, DateTime::TimeFractionFormat::Truncate); keyInfo.DelegatedUserTid = options.DelegatedUserTid; - auto response = _detail::InvokeStorageProtocol([&] { + auto response = _internal::InvokeStorageProtocol([&] { return _detail::QueuesClient(m_serviceUrl, m_pipeline) .GetUserDelegationKeyService(keyInfo, {}, context); }); diff --git a/sdk/storage/azure-storage-queues/test/ut/queue_protocol_test.cpp b/sdk/storage/azure-storage-queues/test/ut/queue_protocol_test.cpp index 391b7138ec..93a5d373c8 100644 --- a/sdk/storage/azure-storage-queues/test/ut/queue_protocol_test.cpp +++ b/sdk/storage/azure-storage-queues/test/ut/queue_protocol_test.cpp @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -#include "../../src/private/queue_protocol.hpp" - #include #include #include @@ -348,49 +346,6 @@ namespace Azure { namespace Storage { namespace Test { namespace { EXPECT_EQ(Transport->Requests[1].Url.GetQueryParameters().at("marker"), "next"); } - TEST(QueueProtocolAdapterTest, PreservesExistingExceptions) - { - try - { - Queues::_detail::InvokeStorageProtocol([] { - StorageException error("existing Storage exception"); - error.ErrorCode = "Existing"; - error.AdditionalInformation["Detail"] = "retained"; - error.RawResponse - = std::make_unique(1, 1, Http::HttpStatusCode::Conflict, "Conflict"); - throw error; - }); - FAIL() << "Expected StorageException"; - } - catch (StorageException const& error) - { - EXPECT_STREQ(error.what(), "existing Storage exception"); - EXPECT_EQ(error.ErrorCode, "Existing"); - EXPECT_EQ(error.AdditionalInformation.at("Detail"), "retained"); - ASSERT_NE(error.RawResponse, nullptr); - } - try - { - Queues::_detail::InvokeStorageProtocol( - [] { throw Core::RequestFailedException("response parsing failed"); }); - FAIL() << "Expected Core::RequestFailedException"; - } - catch (StorageException const&) - { - FAIL() << "An exception without an HTTP response must not be translated"; - } - catch (Core::RequestFailedException const& error) - { - EXPECT_STREQ(error.what(), "response parsing failed"); - EXPECT_EQ(error.RawResponse, nullptr); - } - EXPECT_THROW( - Queues::_detail::InvokeStorageProtocol([] { throw std::logic_error("unrelated"); }), - std::logic_error); - auto value = Queues::_detail::InvokeStorageProtocol([] { return std::make_unique(42); }); - EXPECT_EQ(*value, 42); - } - TEST_F(QueueProtocolTest, CancellationAndTransportFailures) { QueueClient client(Endpoint, Options()); diff --git a/sdk/storage/azure-storage-queues/vcpkg.json b/sdk/storage/azure-storage-queues/vcpkg.json index 8e228fdb71..0f67aac80a 100644 --- a/sdk/storage/azure-storage-queues/vcpkg.json +++ b/sdk/storage/azure-storage-queues/vcpkg.json @@ -8,7 +8,10 @@ "homepage": "https://github.com/Azure/azure-sdk-for-cpp/tree/main/sdk/storage/azure-storage-queues", "license": "MIT", "dependencies": [ - "azure-storage-common-cpp", + { + "name": "azure-storage-common-cpp", + "version>=": "12.16.0-beta.1" + }, { "name": "vcpkg-cmake", "host": true diff --git a/sdk/storage/azure-storage-queues/vcpkg/Config.cmake.in b/sdk/storage/azure-storage-queues/vcpkg/Config.cmake.in index 5ca8fb0e86..e14571f1ae 100644 --- a/sdk/storage/azure-storage-queues/vcpkg/Config.cmake.in +++ b/sdk/storage/azure-storage-queues/vcpkg/Config.cmake.in @@ -4,7 +4,7 @@ @PACKAGE_INIT@ include(CMakeFindDependencyMacro) -find_dependency(azure-storage-common-cpp) +find_dependency(azure-storage-common-cpp 12.16.0) include("${CMAKE_CURRENT_LIST_DIR}/azure-storage-queues-cppTargets.cmake") diff --git a/sdk/storage/azure-storage-queues/vcpkg/vcpkg.json b/sdk/storage/azure-storage-queues/vcpkg/vcpkg.json index 471a34c6ab..d4f841e037 100644 --- a/sdk/storage/azure-storage-queues/vcpkg/vcpkg.json +++ b/sdk/storage/azure-storage-queues/vcpkg/vcpkg.json @@ -18,7 +18,7 @@ { "name": "azure-storage-common-cpp", "default-features": false, - "version>=": "12.12.0" + "version>=": "12.16.0-beta.1" }, { "name": "vcpkg-cmake", From 671a0c8300b7c614c55c72fba030f62934d9c6fe Mon Sep 17 00:00:00 2001 From: Jinming Hu Date: Sat, 24 Oct 2026 14:57:54 +1100 Subject: [PATCH 09/13] Remove migration protocol tests and defer dependency versions Keep the Common helper and Queue runtime unchanged; leave vcpkg and dependency version updates to the release pipeline. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../test/ut/CMakeLists.txt | 1 - .../test/ut/storage_protocol_test.cpp | 149 ----- .../azure-storage-queues/CMakeLists.txt | 4 +- .../test/ut/CMakeLists.txt | 1 - .../test/ut/queue_protocol_test.cpp | 627 ------------------ sdk/storage/azure-storage-queues/vcpkg.json | 5 +- .../vcpkg/Config.cmake.in | 2 +- .../azure-storage-queues/vcpkg/vcpkg.json | 2 +- 8 files changed, 5 insertions(+), 786 deletions(-) delete mode 100644 sdk/storage/azure-storage-common/test/ut/storage_protocol_test.cpp delete mode 100644 sdk/storage/azure-storage-queues/test/ut/queue_protocol_test.cpp diff --git a/sdk/storage/azure-storage-common/test/ut/CMakeLists.txt b/sdk/storage/azure-storage-common/test/ut/CMakeLists.txt index d6116077b3..ccce4f50f9 100644 --- a/sdk/storage/azure-storage-common/test/ut/CMakeLists.txt +++ b/sdk/storage/azure-storage-common/test/ut/CMakeLists.txt @@ -19,7 +19,6 @@ add_executable ( crypt_functions_test.cpp metadata_test.cpp storage_credential_test.cpp - storage_protocol_test.cpp storage_url_test.cpp structured_message_test.cpp test_base.cpp diff --git a/sdk/storage/azure-storage-common/test/ut/storage_protocol_test.cpp b/sdk/storage/azure-storage-common/test/ut/storage_protocol_test.cpp deleted file mode 100644 index dad682a472..0000000000 --- a/sdk/storage/azure-storage-common/test/ut/storage_protocol_test.cpp +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include - -#include -#include -#include - -#include - -namespace Azure { namespace Storage { namespace Test { - namespace Http = Core::Http; - - TEST(StorageProtocol, PreservesReturnTypesAndForwarding) - { - bool invoked = false; - _internal::InvokeStorageProtocol([&] { invoked = true; }); - EXPECT_TRUE(invoked); - EXPECT_EQ(_internal::InvokeStorageProtocol([] { return 42; }), 42); - auto value = _internal::InvokeStorageProtocol([] { return std::make_unique(42); }); - EXPECT_EQ(*value, 42); - - int number = 7; - auto reference = [&]() -> int& { return number; }; - static_assert( - std::is_same::value, - "Preserve reference return types"); - EXPECT_EQ(&_internal::InvokeStorageProtocol(reference), &number); - auto rvalueReference = [&]() -> int&& { return std::move(number); }; - static_assert( - std::is_same::value, - "Preserve rvalue reference return types"); - - struct MoveOnlyOperation - { - std::unique_ptr Value; - explicit MoveOnlyOperation(int value) : Value(std::make_unique(value)) {} - int operator()() && { return *Value; } - int operator()() & = delete; - }; - EXPECT_EQ(_internal::InvokeStorageProtocol(MoveOnlyOperation(9)), 9); - } - - TEST(StorageProtocol, PreservesExistingExceptions) - { - StorageException const* existing = nullptr; - try - { - _internal::InvokeStorageProtocol([&] { - try - { - StorageException error("existing Storage exception"); - error.ErrorCode = "Existing"; - error.AdditionalInformation["Detail"] = "retained"; - error.RawResponse = std::make_unique( - 1, 1, Http::HttpStatusCode::Conflict, "Conflict"); - throw error; - } - catch (StorageException const& error) - { - existing = &error; - throw; - } - }); - FAIL() << "Expected StorageException"; - } - catch (StorageException const& error) - { - EXPECT_EQ(&error, existing); - EXPECT_STREQ(error.what(), "existing Storage exception"); - EXPECT_EQ(error.ErrorCode, "Existing"); - EXPECT_EQ(error.AdditionalInformation.at("Detail"), "retained"); - ASSERT_NE(error.RawResponse, nullptr); - } - Core::RequestFailedException const* withoutResponse = nullptr; - try - { - _internal::InvokeStorageProtocol([&] { - try - { - throw Core::RequestFailedException("response parsing failed"); - } - catch (Core::RequestFailedException const& error) - { - withoutResponse = &error; - throw; - } - }); - FAIL() << "Expected Core::RequestFailedException"; - } - catch (StorageException const&) - { - FAIL() << "An exception without an HTTP response must not be translated"; - } - catch (Core::RequestFailedException const& error) - { - EXPECT_EQ(&error, withoutResponse); - EXPECT_STREQ(error.what(), "response parsing failed"); - EXPECT_EQ(error.RawResponse, nullptr); - } - EXPECT_THROW( - _internal::InvokeStorageProtocol([] { throw std::logic_error("unrelated"); }), - std::logic_error); - } - - TEST(StorageProtocol, ConvertsResponseAndTransfersOwnership) - { - auto response = std::make_unique( - 1, 1, Http::HttpStatusCode::BadRequest, "Bad Request"); - response->SetHeader("content-type", "application/xml"); - response->SetHeader("x-ms-request-id", "request-id"); - response->SetHeader("x-ms-client-request-id", "client-id"); - std::string const body = "InvalidMetadataInvalid value" - "retained"; - response->SetBody(std::vector(body.begin(), body.end())); - Http::RawResponse const* originalResponse = nullptr; - try - { - _internal::InvokeStorageProtocol([&] { - try - { - Core::RequestFailedException error("generated failure"); - error.RawResponse = std::move(response); - throw error; - } - catch (Core::RequestFailedException const& error) - { - originalResponse = error.RawResponse.get(); - throw; - } - }); - FAIL() << "Expected StorageException"; - } - catch (StorageException const& error) - { - EXPECT_EQ(error.RawResponse.get(), originalResponse); - EXPECT_EQ(error.StatusCode, Http::HttpStatusCode::BadRequest); - EXPECT_EQ(error.ErrorCode, "InvalidMetadata"); - EXPECT_EQ(error.Message, "Invalid value"); - EXPECT_EQ(error.RequestId, "request-id"); - EXPECT_EQ(error.ClientRequestId, "client-id"); - EXPECT_EQ(error.AdditionalInformation.at("Detail"), "retained"); - ASSERT_NE(error.RawResponse, nullptr); - EXPECT_EQ(error.RawResponse->GetHeaders().at("content-type"), "application/xml"); - } - EXPECT_EQ(response, nullptr); - } -}}} // namespace Azure::Storage::Test diff --git a/sdk/storage/azure-storage-queues/CMakeLists.txt b/sdk/storage/azure-storage-queues/CMakeLists.txt index 6e73185e15..69b1ee1565 100644 --- a/sdk/storage/azure-storage-queues/CMakeLists.txt +++ b/sdk/storage/azure-storage-queues/CMakeLists.txt @@ -33,9 +33,9 @@ if(FETCH_SOURCE_DEPS) add_subdirectory(${oneFolder} EXCLUDE_FROM_ALL) endforeach() elseif(NOT AZ_ALL_LIBRARIES) - find_package(azure-storage-common-cpp 12.16.0 CONFIG QUIET) + find_package(azure-storage-common-cpp CONFIG QUIET) if(NOT azure-storage-common-cpp_FOUND) - find_package(azure-storage-common-cpp 12.16.0 REQUIRED) + find_package(azure-storage-common-cpp REQUIRED) endif() endif() diff --git a/sdk/storage/azure-storage-queues/test/ut/CMakeLists.txt b/sdk/storage/azure-storage-queues/test/ut/CMakeLists.txt index b9efd48426..790cd80d3d 100644 --- a/sdk/storage/azure-storage-queues/test/ut/CMakeLists.txt +++ b/sdk/storage/azure-storage-queues/test/ut/CMakeLists.txt @@ -21,7 +21,6 @@ add_executable ( queue_client_test.cpp queue_client_test.hpp queue_models_test.cpp - queue_protocol_test.cpp queue_sas_test.cpp queue_service_client_test.cpp queue_service_client_test.hpp diff --git a/sdk/storage/azure-storage-queues/test/ut/queue_protocol_test.cpp b/sdk/storage/azure-storage-queues/test/ut/queue_protocol_test.cpp deleted file mode 100644 index 93a5d373c8..0000000000 --- a/sdk/storage/azure-storage-queues/test/ut/queue_protocol_test.cpp +++ /dev/null @@ -1,627 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -namespace Azure { namespace Storage { namespace Test { namespace { - namespace Http = Core::Http; - using namespace Queues; - - Core::Context::Key const ContextKey; - char const* const Date = "Sun, 18 Oct 2026 10:20:30 GMT"; - char const* const Endpoint - = "https://account.queue.example/queue?sv=2026-04-06&sig=not-a-secret%2Bsample%3D"; - - struct Reply - { - Http::HttpStatusCode Status; - std::string Body; - Core::CaseInsensitiveMap Headers; - }; - - struct RecordedRequest - { - Core::Url Url; - Core::CaseInsensitiveMap Headers; - std::string Body; - std::string ContextValue; - bool HasReplicaStatus; - }; - - class ProtocolTransport final : public Http::HttpTransport { - public: - std::deque Replies; - std::vector Requests; - - std::unique_ptr Send(Http::Request& request, Core::Context const& context) - override - { - context.ThrowIfCancelled(); - if (Replies.empty()) - { - throw std::runtime_error("Unexpected request in Queue protocol test."); - } - std::string contextValue; - context.TryGetValue(ContextKey, contextValue); - std::shared_ptr replica; - auto const hasReplica - = context.TryGetValue(_internal::SecondaryHostReplicaStatusKey, replica); - std::string body; - if (auto stream = request.GetBodyStream()) - { - auto bytes = stream->ReadToEnd(context); - body.assign(bytes.begin(), bytes.end()); - } - Requests.push_back( - {request.GetUrl(), - request.GetHeaders(), - std::move(body), - std::move(contextValue), - hasReplica}); - auto reply = std::move(Replies.front()); - Replies.pop_front(); - auto response = std::make_unique(1, 1, reply.Status, "Test response"); - response->SetHeader("date", Date); - response->SetHeader("x-ms-version", "2026-04-06"); - response->SetHeader("x-ms-request-id", "test-request"); - if (!reply.Body.empty()) - { - response->SetHeader("content-type", "application/xml"); - } - for (auto const& header : reply.Headers) - { - response->SetHeader(header.first, header.second); - } - response->SetBody(std::vector(reply.Body.begin(), reply.Body.end())); - response->SetBodyStream(std::make_unique(response->GetBody())); - return response; - } - }; - - class CountingPolicy final : public Http::Policies::HttpPolicy { - private: - std::shared_ptr m_count; - - public: - explicit CountingPolicy(std::shared_ptr count) : m_count(std::move(count)) {} - - std::unique_ptr Clone() const override - { - return std::make_unique(m_count); - } - - std::unique_ptr Send( - Http::Request& request, - Http::Policies::NextHttpPolicy nextPolicy, - Core::Context const& context) const override - { - ++*m_count; - request.SetHeader("x-test-policy", "once"); - return nextPolicy.Send(request, context); - } - }; - - class QueueProtocolTest : public ::testing::Test { - protected: - std::shared_ptr Transport = std::make_shared(); - - QueueClientOptions Options() - { - QueueClientOptions options; - options.Transport.Transport = Transport; - options.Retry.MaxRetries = 0; - return options; - } - }; - - TEST_F(QueueProtocolTest, MetadataVersionSasAndIdempotency) - { - auto options = Options(); - options.ApiVersion = ServiceVersion::V2019_12_12; - auto count = std::make_shared(0); - options.PerOperationPolicies.emplace_back(std::make_unique(count)); - QueueClient client(Endpoint, options); - Transport->Replies - = {{Http::HttpStatusCode::Created, "", {}}, - {Http::HttpStatusCode::NoContent, "", {}}, - {Http::HttpStatusCode::Conflict, - "QueueAlreadyExistsExists", - {}}, - {Http::HttpStatusCode::NoContent, "", {}}, - {Http::HttpStatusCode::NotFound, - "QueueNotFoundMissing", - {}}, - {Http::HttpStatusCode::NoContent, "", {}}, - {Http::HttpStatusCode::BadRequest, - "InvalidMetadataInvalid", - {}}}; - - CreateQueueOptions create; - create.Metadata = {{"Name", "value"}, {"Empty", ""}}; - auto context = Core::Context().WithValue(ContextKey, std::string("forwarded")); - EXPECT_TRUE(client.Create(create, context).Value.Created); - EXPECT_FALSE(client.Create().Value.Created); - EXPECT_FALSE(client.Create().Value.Created); - EXPECT_TRUE(client.Delete().Value.Deleted); - EXPECT_FALSE(client.Delete().Value.Deleted); - client.SetMetadata({{"Other", "value"}}); - try - { - client.SetMetadata({}); - FAIL() << "Expected StorageException"; - } - catch (StorageException const& error) - { - EXPECT_EQ(error.ErrorCode, "InvalidMetadata"); - EXPECT_EQ(error.StatusCode, Http::HttpStatusCode::BadRequest); - ASSERT_NE(error.RawResponse, nullptr); - EXPECT_EQ(error.RawResponse->GetHeaders().at("x-ms-request-id"), "test-request"); - } - ASSERT_EQ(Transport->Requests.size(), 7U); - EXPECT_EQ(*count, 7); - EXPECT_EQ(Transport->Requests[0].ContextValue, "forwarded"); - EXPECT_EQ(Transport->Requests[0].Headers.at("x-ms-meta-name"), "value"); - EXPECT_EQ(Transport->Requests[0].Headers.at("x-ms-meta-empty"), ""); - EXPECT_EQ(Transport->Requests[5].Headers.at("x-ms-meta-other"), "value"); - EXPECT_EQ(Transport->Requests[5].Url.GetQueryParameters().at("comp"), "metadata"); - for (auto const& request : Transport->Requests) - { - EXPECT_EQ(request.Headers.at("x-ms-version"), "2019-12-12"); - EXPECT_EQ(request.Headers.at("x-test-policy"), "once"); - EXPECT_EQ(request.Headers.count("Accept"), 0U); - EXPECT_EQ(request.Url.GetQueryParameters().at("sig"), "not-a-secret%2Bsample%3D"); - } - } - - TEST_F(QueueProtocolTest, ApproximateMessageCountsAndMetadata) - { - QueueClient client(Endpoint, Options()); - Transport->Replies - = {{Http::HttpStatusCode::Ok, - "", - {{"x-ms-approximate-messages-count", "4294967296"}, {"x-ms-meta-name", "value"}}}, - {Http::HttpStatusCode::Ok, "", {{"x-ms-approximate-messages-count", "0"}}}, - {Http::HttpStatusCode::Ok, "", {}}}; - auto response = client.GetProperties(); - EXPECT_EQ(response.Value.ApproximateMessageCount, -1); - EXPECT_EQ(response.Value.ApproximateMessageCountLong, INT64_C(4294967296)); - EXPECT_EQ(response.Value.Metadata.at("name"), "value"); - EXPECT_EQ(client.GetProperties().Value.ApproximateMessageCount, 0); - EXPECT_THROW(client.GetProperties(), std::out_of_range); - } - - TEST_F(QueueProtocolTest, UserHeaderPoliciesTakePrecedence) - { - class HeaderPolicy final : public Http::Policies::HttpPolicy { - public: - std::unique_ptr Clone() const override - { - return std::make_unique(); - } - - std::unique_ptr Send( - Http::Request& request, - Http::Policies::NextHttpPolicy nextPolicy, - Core::Context const& context) const override - { - request.SetHeader("Accept", "application/custom"); - request.SetHeader("Content-Type", "application/custom"); - return nextPolicy.Send(request, context); - } - }; - auto options = Options(); - options.PerOperationPolicies.emplace_back(std::make_unique()); - QueueClient client(Endpoint, options); - QueueServiceClient service("https://account.queue.example", options); - Transport->Replies - = {{Http::HttpStatusCode::Created, "", {}}, - {Http::HttpStatusCode::Created, "", {}}}; - client.EnqueueMessage("message"); - service.GetQueueClient("queue").EnqueueMessage("message"); - ASSERT_EQ(Transport->Requests.size(), 2U); - for (auto const& request : Transport->Requests) - { - EXPECT_EQ(request.Headers.at("Accept"), "application/custom"); - EXPECT_EQ(request.Headers.at("Content-Type"), "application/custom"); - } - } - - TEST_F(QueueProtocolTest, StorageErrorsAcrossOperations) - { - QueueClient client(Endpoint, Options()); - QueueServiceClient service("https://account.queue.example", Options()); - auto const expiresOn = DateTime::Parse("2026-10-19T10:20:30Z", DateTime::DateFormat::Rfc3339); - std::vector>> const operations{ - {"Create", [&] { client.Create(); }}, - {"Delete", [&] { client.Delete(); }}, - {"GetProperties", [&] { client.GetProperties(); }}, - {"SetMetadata", [&] { client.SetMetadata({}); }}, - {"GetAccessPolicy", [&] { client.GetAccessPolicy(); }}, - {"SetAccessPolicy", [&] { client.SetAccessPolicy({}); }}, - {"EnqueueMessage", [&] { client.EnqueueMessage("message"); }}, - {"ReceiveMessages", [&] { client.ReceiveMessages(); }}, - {"PeekMessages", [&] { client.PeekMessages(); }}, - {"UpdateMessage", [&] { client.UpdateMessage("id", "receipt", std::chrono::seconds(60)); }}, - {"DeleteMessage", [&] { client.DeleteMessage("id", "receipt"); }}, - {"ClearMessages", [&] { client.ClearMessages(); }}, - {"ListQueues", [&] { service.ListQueues(); }}, - {"SetServiceProperties", [&] { service.SetProperties({}); }}, - {"GetServiceProperties", [&] { service.GetProperties(); }}, - {"GetStatistics", [&] { service.GetStatistics(); }}, - {"GetUserDelegationKey", [&] { service.GetUserDelegationKey(expiresOn); }}, - {"CreateQueue", [&] { service.CreateQueue("queue"); }}, - {"DeleteQueue", [&] { service.DeleteQueue("queue"); }}}; - for (auto const& operation : operations) - { - SCOPED_TRACE(operation.first); - Transport->Replies.push_back( - {Http::HttpStatusCode::BadRequest, - "InvalidMetadataInvalid metadata" - "extra detail", - {{"x-ms-client-request-id", "client-request"}}}); - try - { - operation.second(); - FAIL() << "Expected StorageException"; - } - catch (StorageException const& error) - { - EXPECT_EQ(error.ErrorCode, "InvalidMetadata"); - EXPECT_EQ(error.Message, "Invalid metadata"); - EXPECT_EQ(error.StatusCode, Http::HttpStatusCode::BadRequest); - EXPECT_EQ(error.RequestId, "test-request"); - EXPECT_EQ(error.ClientRequestId, "client-request"); - EXPECT_EQ(error.AdditionalInformation.at("Detail"), "extra detail"); - ASSERT_NE(error.RawResponse, nullptr); - EXPECT_EQ(error.RawResponse->GetStatusCode(), Http::HttpStatusCode::BadRequest); - } - } - EXPECT_EQ(Transport->Requests.size(), operations.size()); - EXPECT_TRUE(Transport->Replies.empty()); - } - - TEST_F(QueueProtocolTest, JsonTextAndEmptyErrors) - { - QueueClient client(Endpoint, Options()); - Transport->Replies - = {{Http::HttpStatusCode::BadRequest, - R"({"error":{"code":"InvalidMetadata","message":"JSON error"}})", - {{"content-type", "application/json"}}}, - {Http::HttpStatusCode::BadRequest, - "Text error", - {{"content-type", "text/plain"}, {"x-ms-error-code", "InvalidMetadata"}}}, - {Http::HttpStatusCode::BadRequest, "", {{"x-ms-error-code", "InvalidMetadata"}}}}; - for (auto const& message : {"JSON error", "Text error", ""}) - { - try - { - client.SetMetadata({}); - FAIL() << "Expected StorageException"; - } - catch (StorageException const& error) - { - EXPECT_EQ(error.ErrorCode, "InvalidMetadata"); - EXPECT_EQ(error.Message, message); - ASSERT_NE(error.RawResponse, nullptr); - EXPECT_EQ(error.RawResponse->GetStatusCode(), Http::HttpStatusCode::BadRequest); - } - } - EXPECT_TRUE(Transport->Replies.empty()); - } - - TEST_F(QueueProtocolTest, ContinuationErrors) - { - QueueServiceClient client("https://account.queue.example", Options()); - Transport->Replies - = {{Http::HttpStatusCode::Ok, - "" - "next", - {}}, - {Http::HttpStatusCode::BadRequest, - "InvalidMarkerInvalid marker", - {}}}; - auto page = client.ListQueues(); - try - { - page.MoveToNextPage(); - FAIL() << "Expected StorageException"; - } - catch (StorageException const& error) - { - EXPECT_EQ(error.ErrorCode, "InvalidMarker"); - EXPECT_EQ(error.Message, "Invalid marker"); - ASSERT_NE(error.RawResponse, nullptr); - } - ASSERT_EQ(Transport->Requests.size(), 2U); - EXPECT_EQ(Transport->Requests[1].Url.GetQueryParameters().at("marker"), "next"); - } - - TEST_F(QueueProtocolTest, CancellationAndTransportFailures) - { - QueueClient client(Endpoint, Options()); - auto const cancelled = Core::Context::ApplicationContext.WithDeadline( - std::chrono::system_clock::now() - std::chrono::seconds(1)); - EXPECT_THROW(client.GetProperties({}, cancelled), Core::OperationCancelledException); - EXPECT_TRUE(Transport->Requests.empty()); - EXPECT_THROW(client.GetProperties(), std::runtime_error); - EXPECT_TRUE(Transport->Requests.empty()); - } - - TEST_F(QueueProtocolTest, MessageModelsAndVisibilityOnlyUpdates) - { - QueueClient client(Endpoint, Options()); - std::string const message - = "idSun, 18 Oct 2026 10:20:30 GMT" - "Sun, 25 Oct 2026 10:20:30 GMT" - "4294967296a&b"; - std::string const received = "" + message - + "receipt" - "Sun, 18 Oct 2026 10:21:30 GMT" - ""; - Core::CaseInsensitiveMap const updatedHeaders{ - {"x-ms-popreceipt", "updated"}, - {"x-ms-time-next-visible", "Sun, 18 Oct 2026 10:21:30 GMT"}}; - Transport->Replies - = {{Http::HttpStatusCode::Ok, received, {}}, - {Http::HttpStatusCode::Ok, - "" + message + "", - {}}, - {Http::HttpStatusCode::Created, received, {}}, - {Http::HttpStatusCode::NoContent, "", updatedHeaders}, - {Http::HttpStatusCode::NoContent, "", updatedHeaders}, - {Http::HttpStatusCode::NoContent, "", updatedHeaders}, - {Http::HttpStatusCode::NoContent, "", {}}, - {Http::HttpStatusCode::NoContent, "", {}}}; - ReceiveMessagesOptions receive; - receive.MaxMessages = INT64_C(2147483648); - auto messages = client.ReceiveMessages(receive); - ASSERT_EQ(messages.Value.Messages.size(), 1U); - EXPECT_EQ(messages.Value.Messages[0].DequeueCount, INT64_C(4294967296)); - EXPECT_EQ(messages.Value.Messages[0].MessageText, "a&b"); - EXPECT_EQ(messages.Value.Messages[0].PopReceipt, "receipt"); - PeekMessagesOptions peek; - peek.MaxMessages = INT64_C(2147483648); - auto peeked = client.PeekMessages(peek); - ASSERT_EQ(peeked.Value.Messages.size(), 1U); - EXPECT_EQ(peeked.Value.Messages[0].InsertedOn, messages.Value.Messages[0].InsertedOn); - EXPECT_EQ(peeked.Value.Messages[0].DequeueCount, INT64_C(4294967296)); - auto sent = client.EnqueueMessage("a&b"); - EXPECT_EQ(sent.Value.MessageId, "id"); - auto updated = client.UpdateMessage("id", "receipt", std::chrono::seconds(60)); - EXPECT_EQ(updated.Value.PopReceipt, "updated"); - UpdateMessageOptions update; - update.MessageText = ""; - client.UpdateMessage("id", "receipt", std::chrono::seconds(60), update); - update.MessageText = "a&b"; - client.UpdateMessage("id", "receipt", std::chrono::seconds(60), update); - client.DeleteMessage("id", "receipt"); - client.ClearMessages(); - ASSERT_EQ(Transport->Requests.size(), 8U); - EXPECT_EQ(Transport->Requests[0].Url.GetQueryParameters().at("numofmessages"), "2147483648"); - EXPECT_EQ(Transport->Requests[1].Url.GetQueryParameters().at("numofmessages"), "2147483648"); - EXPECT_EQ(Transport->Requests[1].Url.GetQueryParameters().at("peekonly"), "true"); - EXPECT_EQ(Transport->Requests[3].Body, ""); - EXPECT_EQ(Transport->Requests[3].Headers.count("Content-Type"), 0U); - auto const length = Transport->Requests[3].Headers.find("content-length"); - if (length != Transport->Requests[3].Headers.end()) - { - EXPECT_EQ(length->second, "0"); - } - EXPECT_NE(Transport->Requests[4].Body.find("MessageText"), std::string::npos); - EXPECT_NE(Transport->Requests[5].Body.find("a&b"), std::string::npos); - for (auto const index : {0, 1, 2}) - { - EXPECT_EQ(Transport->Requests[index].Headers.at("Accept"), "application/xml"); - } - for (auto const index : {2, 4, 5}) - { - EXPECT_EQ(Transport->Requests[index].Headers.at("Content-Type"), "application/xml"); - } - for (auto const index : {0, 1, 6, 7}) - { - EXPECT_EQ(Transport->Requests[index].Headers.count("Content-Type"), 0U); - } - for (auto const index : {3, 4, 5, 6, 7}) - { - EXPECT_EQ(Transport->Requests[index].Headers.count("Accept"), 0U); - } - EXPECT_EQ(Transport->Requests[6].Url.GetQueryParameters().at("popreceipt"), "receipt"); - for (auto const& request : Transport->Requests) - { - EXPECT_EQ(request.Url.GetQueryParameters().at("sig"), "not-a-secret%2Bsample%3D"); - } - } - - TEST_F(QueueProtocolTest, AccessPolicyDatesAndEmptyLists) - { - QueueClient client(Endpoint, Options()); - Transport->Replies - = {{Http::HttpStatusCode::NoContent, "", {}}, - {Http::HttpStatusCode::Ok, - "id" - "2026-10-18T10:20:30Z2026-10-19T10:20:30Z" - "ra", - {}}, - {Http::HttpStatusCode::NoContent, "", {}}}; - Models::SignedIdentifier identifier; - identifier.Id = "id"; - identifier.StartsOn - = DateTime::Parse("2026-10-18T10:20:30.1234567Z", DateTime::DateFormat::Rfc3339); - identifier.ExpiresOn - = DateTime::Parse("2026-10-19T10:20:30.1234567Z", DateTime::DateFormat::Rfc3339); - identifier.Permissions = "ra"; - Models::QueueAccessPolicy policy; - policy.SignedIdentifiers.emplace_back(std::move(identifier)); - client.SetAccessPolicy(policy); - auto response = client.GetAccessPolicy(); - ASSERT_EQ(response.Value.SignedIdentifiers.size(), 1U); - EXPECT_EQ(response.Value.SignedIdentifiers[0].Permissions, "ra"); - EXPECT_EQ( - response.Value.SignedIdentifiers[0].StartsOn.Value(), - DateTime::Parse("2026-10-18T10:20:30Z", DateTime::DateFormat::Rfc3339)); - client.SetAccessPolicy({}); - ASSERT_EQ(Transport->Requests.size(), 3U); - EXPECT_NE( - Transport->Requests[0].Body.find("2026-10-18T10:20:30.1234567Z"), - std::string::npos); - EXPECT_EQ( - Transport->Requests[0].Body.find(""), - std::string::npos); - EXPECT_NE(Transport->Requests[2].Body.find("SignedIdentifiers"), std::string::npos); - } - - TEST_F(QueueProtocolTest, AccessPolicyPreservesFractionalSecondZeros) - { - QueueClient client(Endpoint, Options()); - for (auto const* fraction : {"0000000", "1230000", "1234567"}) - { - SCOPED_TRACE(fraction); - auto const timestamp = std::string("2026-10-18T10:20:30.") + fraction + "Z"; - Models::SignedIdentifier identifier; - identifier.Id = "id"; - identifier.StartsOn = DateTime::Parse(timestamp, DateTime::DateFormat::Rfc3339); - identifier.ExpiresOn = identifier.StartsOn; - identifier.Permissions = "ra"; - Models::QueueAccessPolicy policy; - policy.SignedIdentifiers.emplace_back(std::move(identifier)); - Transport->Replies.push_back({Http::HttpStatusCode::NoContent, "", {}}); - client.SetAccessPolicy(policy); - auto const& body = Transport->Requests.back().Body; - EXPECT_NE(body.find("" + timestamp + ""), std::string::npos); - EXPECT_NE(body.find("" + timestamp + ""), std::string::npos); - } - } - - TEST_F(QueueProtocolTest, ServicePropertiesAndStatistics) - { - QueueServiceClient client("https://account.queue.example?sig=not-a-secret", Options()); - Transport->Replies - = {{Http::HttpStatusCode::Accepted, "", {}}, - {Http::HttpStatusCode::Ok, - "1.0true" - "falsetruetrue" - "31.0" - "falsefalse" - "", - {}}, - {Http::HttpStatusCode::Ok, - "live" - "Sun, 18 Oct 2026 10:20:30 GMT" - "", - {}}, - {Http::HttpStatusCode::Ok, - "unavailable" - "", - {}}}; - Models::QueueServiceProperties properties; - properties.Logging.Version = "1.0"; - properties.Logging.RetentionPolicy.IsEnabled = true; - properties.Logging.RetentionPolicy.Days = 3; - properties.HourMetrics.Version = "1.0"; - properties.MinuteMetrics.Version = "1.0"; - client.SetProperties(properties); - auto response = client.GetProperties(); - EXPECT_EQ(response.Value.Logging.Version, "1.0"); - EXPECT_TRUE(response.Value.Logging.Delete); - EXPECT_TRUE(response.Value.Logging.Write); - EXPECT_TRUE(response.Value.Logging.RetentionPolicy.IsEnabled); - EXPECT_EQ(response.Value.Logging.RetentionPolicy.Days.Value(), 3); - EXPECT_FALSE(response.Value.HourMetrics.IsEnabled); - EXPECT_FALSE(response.Value.HourMetrics.IncludeApis.HasValue()); - EXPECT_TRUE(response.Value.Cors.empty()); - auto stats = client.GetStatistics(); - EXPECT_EQ(stats.Value.GeoReplication.Status, Models::GeoReplicationStatus::Live); - ASSERT_TRUE(stats.Value.GeoReplication.LastSyncedOn.HasValue()); - EXPECT_EQ( - stats.Value.GeoReplication.LastSyncedOn.Value(), - DateTime::Parse("2026-10-18T10:20:30Z", DateTime::DateFormat::Rfc3339)); - EXPECT_FALSE(client.GetStatistics().Value.GeoReplication.LastSyncedOn.HasValue()); - ASSERT_EQ(Transport->Requests.size(), 4U); - EXPECT_NE(Transport->Requests[0].Body.find(""), std::string::npos); - EXPECT_EQ(Transport->Requests[0].Headers.at("Content-Type"), "application/xml"); - EXPECT_EQ(Transport->Requests[0].Headers.count("Accept"), 0U); - for (auto const index : {1, 2, 3}) - { - EXPECT_EQ(Transport->Requests[index].Headers.at("Accept"), "application/xml"); - EXPECT_EQ(Transport->Requests[index].Headers.count("Content-Type"), 0U); - } - EXPECT_EQ(Transport->Requests[0].Body.find(""), std::string::npos); - EXPECT_EQ(Transport->Requests[0].Url.GetQueryParameters().at("restype"), "service"); - EXPECT_TRUE(Transport->Requests[1].HasReplicaStatus); - } - - TEST_F(QueueProtocolTest, PagingAndDelegationKey) - { - QueueServiceClient client("https://account.queue.example?sig=not-a-secret", Options()); - Transport->Replies - = {{Http::HttpStatusCode::Ok, - "pre" - "firstvalue" - "next marker", - {}}, - {Http::HttpStatusCode::Ok, - "pre" - "", - {}}, - {Http::HttpStatusCode::Ok, - "objecttenant" - "2026-10-18T10:20:30Z" - "2026-10-19T10:20:30Zq" - "2026-04-06sample-key" - "", - {}}}; - ListQueuesOptions list; - list.Prefix = "pre"; - list.Include = Models::ListQueuesIncludeFlags::Metadata; - auto page = client.ListQueues(list); - ASSERT_EQ(page.Queues.size(), 1U); - EXPECT_EQ(page.ServiceEndpoint, "https://account.queue.example"); - EXPECT_EQ(page.Queues[0].Name, "first"); - EXPECT_EQ(page.Queues[0].Metadata.at("name"), "value"); - EXPECT_EQ(page.Queues[0].Metadata.at("empty"), ""); - EXPECT_EQ(page.NextPageToken.Value(), "next marker"); - page.MoveToNextPage(); - EXPECT_TRUE(page.Queues.empty()); - EXPECT_FALSE(page.NextPageToken.HasValue()); - GetUserDelegationKeyOptions keyOptions; - keyOptions.StartsOn - = DateTime::Parse("2026-10-18T10:20:30.1234567Z", DateTime::DateFormat::Rfc3339); - keyOptions.DelegatedUserTid = "delegated"; - auto key = client.GetUserDelegationKey( - DateTime::Parse("2026-10-19T10:20:30.1234567Z", DateTime::DateFormat::Rfc3339), keyOptions); - EXPECT_EQ(key.Value.SignedObjectId, "object"); - EXPECT_EQ(key.Value.SignedTenantId, "tenant"); - EXPECT_EQ( - key.Value.SignedStartsOn, - DateTime::Parse("2026-10-18T10:20:30Z", DateTime::DateFormat::Rfc3339)); - EXPECT_EQ( - key.Value.SignedExpiresOn, - DateTime::Parse("2026-10-19T10:20:30Z", DateTime::DateFormat::Rfc3339)); - EXPECT_EQ(key.Value.Value, "sample-key"); - ASSERT_EQ(Transport->Requests.size(), 3U); - EXPECT_TRUE(Transport->Requests[0].HasReplicaStatus); - EXPECT_TRUE(Transport->Requests[1].HasReplicaStatus); - EXPECT_EQ(Transport->Requests[0].Url.GetQueryParameters().at("include"), "metadata"); - EXPECT_EQ(Transport->Requests[1].Url.GetQueryParameters().at("marker"), "next%20marker"); - EXPECT_NE( - Transport->Requests[2].Body.find("2026-10-18T10:20:30Z"), std::string::npos); - EXPECT_NE( - Transport->Requests[2].Body.find("2026-10-19T10:20:30Z"), - std::string::npos); - EXPECT_NE( - Transport->Requests[2].Body.find("delegated"), - std::string::npos); - } -}}}} // namespace Azure::Storage::Test:: diff --git a/sdk/storage/azure-storage-queues/vcpkg.json b/sdk/storage/azure-storage-queues/vcpkg.json index 0f67aac80a..8e228fdb71 100644 --- a/sdk/storage/azure-storage-queues/vcpkg.json +++ b/sdk/storage/azure-storage-queues/vcpkg.json @@ -8,10 +8,7 @@ "homepage": "https://github.com/Azure/azure-sdk-for-cpp/tree/main/sdk/storage/azure-storage-queues", "license": "MIT", "dependencies": [ - { - "name": "azure-storage-common-cpp", - "version>=": "12.16.0-beta.1" - }, + "azure-storage-common-cpp", { "name": "vcpkg-cmake", "host": true diff --git a/sdk/storage/azure-storage-queues/vcpkg/Config.cmake.in b/sdk/storage/azure-storage-queues/vcpkg/Config.cmake.in index e14571f1ae..5ca8fb0e86 100644 --- a/sdk/storage/azure-storage-queues/vcpkg/Config.cmake.in +++ b/sdk/storage/azure-storage-queues/vcpkg/Config.cmake.in @@ -4,7 +4,7 @@ @PACKAGE_INIT@ include(CMakeFindDependencyMacro) -find_dependency(azure-storage-common-cpp 12.16.0) +find_dependency(azure-storage-common-cpp) include("${CMAKE_CURRENT_LIST_DIR}/azure-storage-queues-cppTargets.cmake") diff --git a/sdk/storage/azure-storage-queues/vcpkg/vcpkg.json b/sdk/storage/azure-storage-queues/vcpkg/vcpkg.json index d4f841e037..471a34c6ab 100644 --- a/sdk/storage/azure-storage-queues/vcpkg/vcpkg.json +++ b/sdk/storage/azure-storage-queues/vcpkg/vcpkg.json @@ -18,7 +18,7 @@ { "name": "azure-storage-common-cpp", "default-features": false, - "version>=": "12.16.0-beta.1" + "version>=": "12.12.0" }, { "name": "vcpkg-cmake", From 924cae491eb7e849e3a92dbf234633022662b0d3 Mon Sep 17 00:00:00 2001 From: Jinming Hu Date: Sat, 24 Oct 2026 15:01:48 +1100 Subject: [PATCH 10/13] Cover nested Queue model defaults and metadata behavior Add model-only coverage for Queue properties, service defaults, case-insensitive metadata, and explicit message result values. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../test/ut/queue_models_test.cpp | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/sdk/storage/azure-storage-queues/test/ut/queue_models_test.cpp b/sdk/storage/azure-storage-queues/test/ut/queue_models_test.cpp index e0eefc38dd..779720df4f 100644 --- a/sdk/storage/azure-storage-queues/test/ut/queue_models_test.cpp +++ b/sdk/storage/azure-storage-queues/test/ut/queue_models_test.cpp @@ -41,6 +41,13 @@ namespace Azure { namespace Storage { namespace Test { EXPECT_TRUE(Models::ReceivedMessages().Messages.empty()); EXPECT_EQ(Models::PeekedQueueMessage().DequeueCount, 0); EXPECT_TRUE(Models::PeekedMessages().Messages.empty()); + Models::QueueProperties properties; + EXPECT_TRUE(properties.Metadata.empty()); + EXPECT_EQ(properties.ApproximateMessageCount, 0); + EXPECT_EQ(properties.ApproximateMessageCountLong, 0); + Models::ServiceStatistics statistics; + EXPECT_TRUE(statistics.GeoReplication.Status.ToString().empty()); + EXPECT_FALSE(statistics.GeoReplication.LastSyncedOn.HasValue()); Queues::ListQueuesOptions list; EXPECT_FALSE(list.Prefix.HasValue()); EXPECT_FALSE(list.ContinuationToken.HasValue()); @@ -48,6 +55,64 @@ namespace Azure { namespace Storage { namespace Test { EXPECT_EQ(list.Include, Models::ListQueuesIncludeFlags::None); } + TEST(QueueModels, ServicePropertiesDefaults) + { + Queues::Models::QueueServiceProperties properties; + EXPECT_TRUE(properties.Cors.empty()); + EXPECT_TRUE(properties.Logging.Version.empty()); + EXPECT_FALSE(properties.Logging.Delete); + EXPECT_FALSE(properties.Logging.Read); + EXPECT_FALSE(properties.Logging.Write); + EXPECT_FALSE(properties.Logging.RetentionPolicy.IsEnabled); + EXPECT_FALSE(properties.Logging.RetentionPolicy.Days.HasValue()); + for (auto const* metrics : {&properties.HourMetrics, &properties.MinuteMetrics}) + { + EXPECT_TRUE(metrics->Version.empty()); + EXPECT_FALSE(metrics->IsEnabled); + EXPECT_FALSE(metrics->IncludeApis.HasValue()); + EXPECT_FALSE(metrics->RetentionPolicy.IsEnabled); + EXPECT_FALSE(metrics->RetentionPolicy.Days.HasValue()); + } + } + + TEST(QueueModels, MetadataIsCaseInsensitive) + { + Queues::Models::QueueItem item; + Queues::Models::QueueProperties properties; + for (auto* metadata : {&item.Metadata, &properties.Metadata}) + { + EXPECT_TRUE(metadata->emplace("MixedCase", "original").second); + auto const found = metadata->find("mixedcase"); + ASSERT_NE(found, metadata->end()); + EXPECT_EQ(found->second, "original"); + EXPECT_FALSE(metadata->emplace("MIXEDCASE", "duplicate").second); + EXPECT_EQ(metadata->size(), 1U); + (*metadata)["mIxEdCaSe"] = "updated"; + EXPECT_EQ(metadata->size(), 1U); + EXPECT_EQ(metadata->at("MIXEDCASE"), "updated"); + EXPECT_EQ(metadata->at("mixedcase"), "updated"); + } + } + + TEST(QueueModels, MessageResults) + { + auto const inserted = DateTime::Parse("2026-10-24T01:02:03Z", DateTime::DateFormat::Rfc3339); + auto const expires = DateTime::Parse("2026-10-25T01:02:03Z", DateTime::DateFormat::Rfc3339); + auto const visible = DateTime::Parse("2026-10-24T01:03:03Z", DateTime::DateFormat::Rfc3339); + Queues::Models::EnqueueMessageResult enqueued{ + "message-id", inserted, expires, "receipt", visible}; + EXPECT_EQ(enqueued.MessageId, "message-id"); + EXPECT_EQ(enqueued.InsertedOn, inserted); + EXPECT_EQ(enqueued.ExpiresOn, expires); + EXPECT_EQ(enqueued.PopReceipt, "receipt"); + EXPECT_EQ(enqueued.NextVisibleOn, visible); + Queues::Models::UpdateMessageResult updated{"updated-receipt", expires}; + EXPECT_EQ(updated.PopReceipt, "updated-receipt"); + EXPECT_EQ(updated.NextVisibleOn, expires); + EXPECT_EQ(enqueued.PopReceipt, "receipt"); + EXPECT_EQ(enqueued.NextVisibleOn, visible); + } + TEST(QueueModels, Enumerations) { using Queues::Models::GeoReplicationStatus; From adc0ae08f4b85645ca1a3bac3c8ec5238615e3ce Mon Sep 17 00:00:00 2001 From: Jinming Hu Date: Sat, 24 Oct 2026 15:13:44 +1100 Subject: [PATCH 11/13] Align Queue playback request headers with generated protocol Pin header-only Queue recording updates: add XML Accept on XML-response operations and retain generated application/xml Content-Type for XML request bodies. Preserve bodies, responses and default proxy matching. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- sdk/storage/assets.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/storage/assets.json b/sdk/storage/assets.json index 9f08d82d12..db2908f81f 100644 --- a/sdk/storage/assets.json +++ b/sdk/storage/assets.json @@ -2,5 +2,5 @@ "AssetsRepo": "Azure/azure-sdk-assets", "AssetsRepoPrefixPath": "cpp", "TagPrefix": "cpp/storage", - "Tag": "cpp/storage_01f138ad9c" + "Tag": "cpp/storage_a9cbd80fff" } From 7ab98a8a54339b87ebc13ca6f73f47930e30bd36 Mon Sep 17 00:00:00 2001 From: Jinming Hu Date: Sat, 24 Oct 2026 15:22:35 +1100 Subject: [PATCH 12/13] Scope generated Queue spelling term to the Queue package Allow numofmessages through the established package-local CSpell dictionary pattern without changing generated names, global dictionaries or exclusions. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- sdk/storage/azure-storage-queues/cspell.json | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 sdk/storage/azure-storage-queues/cspell.json diff --git a/sdk/storage/azure-storage-queues/cspell.json b/sdk/storage/azure-storage-queues/cspell.json new file mode 100644 index 0000000000..bf89651b12 --- /dev/null +++ b/sdk/storage/azure-storage-queues/cspell.json @@ -0,0 +1,12 @@ +{ + "version": "0.2", + "language": "en", + "languageId": "cpp", + "dictionaries": [ + "powershell", + "cpp" + ], + "words": [ + "numofmessages" + ] +} From 7e0f34383d584d16fccebd71c2023e0690bdc56c Mon Sep 17 00:00:00 2001 From: Jinming Hu Date: Sat, 24 Oct 2026 16:09:23 +1100 Subject: [PATCH 13/13] Fix Queue generated links and public flag documentation Regenerate private documentation with the scoped emitter documentation fix: use safe Markdown links and omit an empty class brief. Document the four public list flags operators without changing declarations or runtime tokens. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../azure/storage/queues/queue_options.hpp | 24 +++++ .../src/generated/queues_client.hpp | 4 - .../src/generated/queues_client_options.hpp | 102 +++++++++--------- 3 files changed, 75 insertions(+), 55 deletions(-) diff --git a/sdk/storage/azure-storage-queues/inc/azure/storage/queues/queue_options.hpp b/sdk/storage/azure-storage-queues/inc/azure/storage/queues/queue_options.hpp index b391c7ad9c..dbdd1f6c73 100644 --- a/sdk/storage/azure-storage-queues/inc/azure/storage/queues/queue_options.hpp +++ b/sdk/storage/azure-storage-queues/inc/azure/storage/queues/queue_options.hpp @@ -33,11 +33,23 @@ namespace Azure { namespace Storage { namespace Queues { None = 0, Metadata = 1, }; + /** + * @brief Combines the flags from both operands. + * @param lhs The left operand. + * @param rhs The right operand. + * @return The combined flags. + */ inline ListQueuesIncludeFlags operator|(ListQueuesIncludeFlags lhs, ListQueuesIncludeFlags rhs) { using type = std::underlying_type_t; return static_cast(static_cast(lhs) | static_cast(rhs)); } + /** + * @brief Adds the right operand's flags to the left operand. + * @param lhs The flags to update. + * @param rhs The flags to add. + * @return A reference to the updated left operand. + */ inline ListQueuesIncludeFlags& operator|=( ListQueuesIncludeFlags& lhs, ListQueuesIncludeFlags rhs) @@ -45,11 +57,23 @@ namespace Azure { namespace Storage { namespace Queues { lhs = lhs | rhs; return lhs; } + /** + * @brief Selects the flags present in both operands. + * @param lhs The left operand. + * @param rhs The right operand. + * @return The flags present in both operands. + */ inline ListQueuesIncludeFlags operator&(ListQueuesIncludeFlags lhs, ListQueuesIncludeFlags rhs) { using type = std::underlying_type_t; return static_cast(static_cast(lhs) & static_cast(rhs)); } + /** + * @brief Retains only the right operand's flags in the left operand. + * @param lhs The flags to update. + * @param rhs The flags to retain. + * @return A reference to the updated left operand. + */ inline ListQueuesIncludeFlags& operator&=( ListQueuesIncludeFlags& lhs, ListQueuesIncludeFlags rhs) diff --git a/sdk/storage/azure-storage-queues/src/generated/queues_client.hpp b/sdk/storage/azure-storage-queues/src/generated/queues_client.hpp index 2878749cbd..3aea69cfd4 100644 --- a/sdk/storage/azure-storage-queues/src/generated/queues_client.hpp +++ b/sdk/storage/azure-storage-queues/src/generated/queues_client.hpp @@ -25,10 +25,6 @@ #include namespace Azure { namespace Storage { namespace Queues { namespace _detail { - /** - * @brief - * - */ class QueuesClient final { public: /** diff --git a/sdk/storage/azure-storage-queues/src/generated/queues_client_options.hpp b/sdk/storage/azure-storage-queues/src/generated/queues_client_options.hpp index d41f8e593e..e8c20c2df9 100644 --- a/sdk/storage/azure-storage-queues/src/generated/queues_client_options.hpp +++ b/sdk/storage/azure-storage-queues/src/generated/queues_client_options.hpp @@ -37,9 +37,9 @@ namespace Azure { namespace Storage { namespace Queues { namespace _detail { /// An opaque, globally-unique, client-generated string identifier for the request. Nullable XMsClientRequestId; - /// The timeout parameter is expressed in seconds. For more information, see Setting - /// Timeouts for Queue Service Operations. + /// The timeout parameter is expressed in seconds. For more information, see [Setting Timeouts + /// for Queue Service + /// Operations.](https://learn.microsoft.com/en-us/rest/api/storageservices/setting-timeouts-for-queue-service-operations) Nullable Timeout; }; @@ -52,9 +52,9 @@ namespace Azure { namespace Storage { namespace Queues { namespace _detail { /// An opaque, globally-unique, client-generated string identifier for the request. Nullable XMsClientRequestId; - /// The timeout parameter is expressed in seconds. For more information, see Setting - /// Timeouts for Queue Service Operations. + /// The timeout parameter is expressed in seconds. For more information, see [Setting Timeouts + /// for Queue Service + /// Operations.](https://learn.microsoft.com/en-us/rest/api/storageservices/setting-timeouts-for-queue-service-operations) Nullable Timeout; }; @@ -67,9 +67,9 @@ namespace Azure { namespace Storage { namespace Queues { namespace _detail { /// An opaque, globally-unique, client-generated string identifier for the request. Nullable XMsClientRequestId; - /// The timeout parameter is expressed in seconds. For more information, see Setting - /// Timeouts for Queue Service Operations. + /// The timeout parameter is expressed in seconds. For more information, see [Setting Timeouts + /// for Queue Service + /// Operations.](https://learn.microsoft.com/en-us/rest/api/storageservices/setting-timeouts-for-queue-service-operations) Nullable Timeout; }; @@ -82,9 +82,9 @@ namespace Azure { namespace Storage { namespace Queues { namespace _detail { /// An opaque, globally-unique, client-generated string identifier for the request. Nullable XMsClientRequestId; - /// The timeout parameter is expressed in seconds. For more information, see Setting - /// Timeouts for Queue Service Operations. + /// The timeout parameter is expressed in seconds. For more information, see [Setting Timeouts + /// for Queue Service + /// Operations.](https://learn.microsoft.com/en-us/rest/api/storageservices/setting-timeouts-for-queue-service-operations) Nullable Timeout; }; @@ -114,9 +114,9 @@ namespace Azure { namespace Storage { namespace Queues { namespace _detail { /// maxresults, or specifies a value greater than 5000, the server will return up to 5000 items. Nullable Maxresults; - /// The timeout parameter is expressed in seconds. For more information, see Setting - /// Timeouts for Queue Service Operations. + /// The timeout parameter is expressed in seconds. For more information, see [Setting Timeouts + /// for Queue Service + /// Operations.](https://learn.microsoft.com/en-us/rest/api/storageservices/setting-timeouts-for-queue-service-operations) Nullable Timeout; /// Specify to include additional, optional information. @@ -132,9 +132,9 @@ namespace Azure { namespace Storage { namespace Queues { namespace _detail { /// An opaque, globally-unique, client-generated string identifier for the request. Nullable XMsClientRequestId; - /// The timeout parameter is expressed in seconds. For more information, see Setting - /// Timeouts for Queue Service Operations. + /// The timeout parameter is expressed in seconds. For more information, see [Setting Timeouts + /// for Queue Service + /// Operations.](https://learn.microsoft.com/en-us/rest/api/storageservices/setting-timeouts-for-queue-service-operations) Nullable Timeout; /// The metadata headers. @@ -150,9 +150,9 @@ namespace Azure { namespace Storage { namespace Queues { namespace _detail { /// An opaque, globally-unique, client-generated string identifier for the request. Nullable XMsClientRequestId; - /// The timeout parameter is expressed in seconds. For more information, see Setting - /// Timeouts for Queue Service Operations. + /// The timeout parameter is expressed in seconds. For more information, see [Setting Timeouts + /// for Queue Service + /// Operations.](https://learn.microsoft.com/en-us/rest/api/storageservices/setting-timeouts-for-queue-service-operations) Nullable Timeout; }; @@ -165,9 +165,9 @@ namespace Azure { namespace Storage { namespace Queues { namespace _detail { /// An opaque, globally-unique, client-generated string identifier for the request. Nullable XMsClientRequestId; - /// The timeout parameter is expressed in seconds. For more information, see Setting - /// Timeouts for Queue Service Operations. + /// The timeout parameter is expressed in seconds. For more information, see [Setting Timeouts + /// for Queue Service + /// Operations.](https://learn.microsoft.com/en-us/rest/api/storageservices/setting-timeouts-for-queue-service-operations) Nullable Timeout; }; @@ -180,9 +180,9 @@ namespace Azure { namespace Storage { namespace Queues { namespace _detail { /// An opaque, globally-unique, client-generated string identifier for the request. Nullable XMsClientRequestId; - /// The timeout parameter is expressed in seconds. For more information, see Setting - /// Timeouts for Queue Service Operations. + /// The timeout parameter is expressed in seconds. For more information, see [Setting Timeouts + /// for Queue Service + /// Operations.](https://learn.microsoft.com/en-us/rest/api/storageservices/setting-timeouts-for-queue-service-operations) Nullable Timeout; /// The metadata headers. @@ -198,9 +198,9 @@ namespace Azure { namespace Storage { namespace Queues { namespace _detail { /// An opaque, globally-unique, client-generated string identifier for the request. Nullable XMsClientRequestId; - /// The timeout parameter is expressed in seconds. For more information, see Setting - /// Timeouts for Queue Service Operations. + /// The timeout parameter is expressed in seconds. For more information, see [Setting Timeouts + /// for Queue Service + /// Operations.](https://learn.microsoft.com/en-us/rest/api/storageservices/setting-timeouts-for-queue-service-operations) Nullable Timeout; }; @@ -213,9 +213,9 @@ namespace Azure { namespace Storage { namespace Queues { namespace _detail { /// An opaque, globally-unique, client-generated string identifier for the request. Nullable XMsClientRequestId; - /// The timeout parameter is expressed in seconds. For more information, see Setting - /// Timeouts for Queue Service Operations. + /// The timeout parameter is expressed in seconds. For more information, see [Setting Timeouts + /// for Queue Service + /// Operations.](https://learn.microsoft.com/en-us/rest/api/storageservices/setting-timeouts-for-queue-service-operations) Nullable Timeout; /// The access control list. @@ -241,9 +241,9 @@ namespace Azure { namespace Storage { namespace Queues { namespace _detail { /// visibility timeout of a message can be set to a value later than the expiry time. Nullable Visibilitytimeout; - /// The timeout parameter is expressed in seconds. For more information, see Setting - /// Timeouts for Queue Service Operations. + /// The timeout parameter is expressed in seconds. For more information, see [Setting Timeouts + /// for Queue Service + /// Operations.](https://learn.microsoft.com/en-us/rest/api/storageservices/setting-timeouts-for-queue-service-operations) Nullable Timeout; }; @@ -256,9 +256,9 @@ namespace Azure { namespace Storage { namespace Queues { namespace _detail { /// An opaque, globally-unique, client-generated string identifier for the request. Nullable XMsClientRequestId; - /// The timeout parameter is expressed in seconds. For more information, see Setting - /// Timeouts for Queue Service Operations. + /// The timeout parameter is expressed in seconds. For more information, see [Setting Timeouts + /// for Queue Service + /// Operations.](https://learn.microsoft.com/en-us/rest/api/storageservices/setting-timeouts-for-queue-service-operations) Nullable Timeout; }; @@ -282,9 +282,9 @@ namespace Azure { namespace Storage { namespace Queues { namespace _detail { /// does not expire. If this parameter is omitted, the default time-to-live is 7 days. Nullable Messagettl; - /// The timeout parameter is expressed in seconds. For more information, see Setting - /// Timeouts for Queue Service Operations. + /// The timeout parameter is expressed in seconds. For more information, see [Setting Timeouts + /// for Queue Service + /// Operations.](https://learn.microsoft.com/en-us/rest/api/storageservices/setting-timeouts-for-queue-service-operations) Nullable Timeout; }; @@ -302,9 +302,9 @@ namespace Azure { namespace Storage { namespace Queues { namespace _detail { /// single message is retrieved from the queue with this operation. Nullable Numofmessages; - /// The timeout parameter is expressed in seconds. For more information, see Setting - /// Timeouts for Queue Service Operations. + /// The timeout parameter is expressed in seconds. For more information, see [Setting Timeouts + /// for Queue Service + /// Operations.](https://learn.microsoft.com/en-us/rest/api/storageservices/setting-timeouts-for-queue-service-operations) Nullable Timeout; }; @@ -317,9 +317,9 @@ namespace Azure { namespace Storage { namespace Queues { namespace _detail { /// An opaque, globally-unique, client-generated string identifier for the request. Nullable XMsClientRequestId; - /// The timeout parameter is expressed in seconds. For more information, see Setting - /// Timeouts for Queue Service Operations. + /// The timeout parameter is expressed in seconds. For more information, see [Setting Timeouts + /// for Queue Service + /// Operations.](https://learn.microsoft.com/en-us/rest/api/storageservices/setting-timeouts-for-queue-service-operations) Nullable Timeout; /// The queue message. The message must be in a format that can be included in an XML request @@ -336,9 +336,9 @@ namespace Azure { namespace Storage { namespace Queues { namespace _detail { /// An opaque, globally-unique, client-generated string identifier for the request. Nullable XMsClientRequestId; - /// The timeout parameter is expressed in seconds. For more information, see Setting - /// Timeouts for Queue Service Operations. + /// The timeout parameter is expressed in seconds. For more information, see [Setting Timeouts + /// for Queue Service + /// Operations.](https://learn.microsoft.com/en-us/rest/api/storageservices/setting-timeouts-for-queue-service-operations) Nullable Timeout; }; }}}} // namespace Azure::Storage::Queues::_detail