2502 exceptions (#2529)
* first pass * move exception creation from keyvault to core * merge fix * format * fix includes location and containers * remove default constructor * to trigger build * undo change * uncomment * Update sdk/core/azure-core/test/ut/exception_test.cpp Co-authored-by: Rick Winter <rick.winter@microsoft.com> Co-authored-by: Rick Winter <rick.winter@microsoft.com>
This commit is contained in:
parent
e51e693640
commit
c449e3f9bc
@ -92,6 +92,18 @@ namespace Azure { namespace Core {
|
||||
const std::string& message,
|
||||
std::unique_ptr<Azure::Core::Http::RawResponse> rawResponse);
|
||||
|
||||
/**
|
||||
* @brief Constructs a new `%RequestFailedException` object with an HTTP raw response.
|
||||
*
|
||||
* @note The HTTP raw response is parsed to populate information expected from all Azure
|
||||
* Services like the status code, reason phrase and some headers like the request ID. A concrete
|
||||
* Service exception which derives from this exception uses its constructor to parse the HTTP
|
||||
* raw response adding the service specific values to the exception.
|
||||
*
|
||||
* @param rawResponse The HTTP raw response from the service.
|
||||
*/
|
||||
explicit RequestFailedException(std::unique_ptr<Azure::Core::Http::RawResponse>& rawResponse);
|
||||
|
||||
/**
|
||||
* @brief Constructs a new `%RequestFailedException` by copying from an existing one.
|
||||
* @note Copies the #Azure::Core::Http::RawResponse into the new `RequestFailedException`.
|
||||
@ -133,5 +145,10 @@ namespace Azure { namespace Core {
|
||||
*
|
||||
*/
|
||||
~RequestFailedException() = default;
|
||||
|
||||
private:
|
||||
std::string getRawResponseField(
|
||||
std::unique_ptr<Azure::Core::Http::RawResponse>& rawResponse,
|
||||
std::string fieldName);
|
||||
};
|
||||
}} // namespace Azure::Core
|
||||
|
||||
@ -3,10 +3,12 @@
|
||||
|
||||
#include "azure/core/exception.hpp"
|
||||
#include "azure/core/http/http.hpp"
|
||||
|
||||
#include "azure/core/http/policies/policy.hpp"
|
||||
#include "azure/core/internal/json/json.hpp"
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
using namespace Azure::Core::Http::_internal;
|
||||
@ -28,4 +30,48 @@ namespace Azure { namespace Core {
|
||||
RawResponse = std::move(rawResponse);
|
||||
}
|
||||
|
||||
RequestFailedException::RequestFailedException(
|
||||
std::unique_ptr<Azure::Core::Http::RawResponse>& rawResponse)
|
||||
: std::runtime_error(getRawResponseField(rawResponse, "message"))
|
||||
{
|
||||
auto& headers = rawResponse->GetHeaders();
|
||||
|
||||
StatusCode = rawResponse->GetStatusCode();
|
||||
ErrorCode = getRawResponseField(rawResponse, "code");
|
||||
StatusCode = rawResponse->GetStatusCode();
|
||||
ReasonPhrase = rawResponse->GetReasonPhrase();
|
||||
RequestId = HttpShared::GetHeaderOrEmptyString(headers, HttpShared::MsRequestId);
|
||||
ClientRequestId = HttpShared::GetHeaderOrEmptyString(headers, HttpShared::MsClientRequestId);
|
||||
Message = this->what();
|
||||
}
|
||||
|
||||
std::string RequestFailedException::getRawResponseField(
|
||||
std::unique_ptr<Azure::Core::Http::RawResponse>& rawResponse,
|
||||
std::string fieldName)
|
||||
{
|
||||
auto& headers = rawResponse->GetHeaders();
|
||||
std::string contentType = HttpShared::GetHeaderOrEmptyString(headers, HttpShared::ContentType);
|
||||
std::vector<uint8_t> bodyBuffer = rawResponse->GetBody();
|
||||
std::string result;
|
||||
|
||||
if (contentType.find("json") != std::string::npos)
|
||||
{
|
||||
auto jsonParser = Azure::Core::Json::_internal::json::parse(bodyBuffer);
|
||||
auto error = jsonParser.find("error");
|
||||
if (error != jsonParser.end() && error.value().contains(fieldName))
|
||||
{
|
||||
result = error.value()[fieldName].get<std::string>();
|
||||
}
|
||||
else if (fieldName == "message")
|
||||
{
|
||||
result = std::string(bodyBuffer.begin(), bodyBuffer.end());
|
||||
}
|
||||
}
|
||||
else if (fieldName == "message")
|
||||
{
|
||||
result = std::string(bodyBuffer.begin(), bodyBuffer.end());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}} // namespace Azure::Core
|
||||
|
||||
@ -73,6 +73,7 @@ add_executable (
|
||||
transport_adapter_implementation_test.cpp
|
||||
url_test.cpp
|
||||
uuid_test.cpp
|
||||
exception_test.cpp
|
||||
)
|
||||
|
||||
if (MSVC)
|
||||
@ -100,7 +101,7 @@ target_link_libraries(azure-core-test PRIVATE azure-core gtest gmock)
|
||||
add_executable (
|
||||
azure-core-global-context-test
|
||||
global_context_test.cpp
|
||||
)
|
||||
)
|
||||
if (MSVC)
|
||||
# Disable gtest warnings for MSVC
|
||||
target_compile_options(azure-core-global-context-test PUBLIC /wd26495 /wd26812 /wd6326 /wd28204 /wd28020 /wd6330 /wd4389)
|
||||
@ -122,7 +123,7 @@ if(BUILD_TRANSPORT_CURL)
|
||||
add_executable (
|
||||
azure-core-libcurl-test
|
||||
azure_libcurl_core_main_test.cpp
|
||||
)
|
||||
)
|
||||
|
||||
if (MSVC)
|
||||
# warning C4389: '==': signed/unsigned mismatch
|
||||
|
||||
79
sdk/core/azure-core/test/ut/exception_test.cpp
Normal file
79
sdk/core/azure-core/test/ut/exception_test.cpp
Normal file
@ -0,0 +1,79 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
#include <azure/core/exception.hpp>
|
||||
#include <azure/core/http/http.hpp>
|
||||
#include <gtest/gtest.h>
|
||||
#include <string>
|
||||
|
||||
using namespace Azure::Core;
|
||||
using namespace Azure::Core::Http::_internal;
|
||||
|
||||
TEST(RequestFailedException, JSONError)
|
||||
{
|
||||
auto response = std::make_unique<Azure::Core::Http::RawResponse>(
|
||||
1, 1, Azure::Core::Http::HttpStatusCode::ServiceUnavailable, "retry please :");
|
||||
static constexpr uint8_t const responseBody[]
|
||||
= "{\"error\":{ \"code\":\"503\", \"message\":\"JT\"}}";
|
||||
static constexpr uint8_t const responseBodyStream[]
|
||||
= "{\"error\":{ \"code\":\"503\", \"message\":\"JT\"}}";
|
||||
|
||||
response->SetHeader(HttpShared::ContentType, "application/json");
|
||||
response->SetHeader(HttpShared::MsRequestId, "1");
|
||||
response->SetHeader(HttpShared::MsClientRequestId, "2");
|
||||
response->SetBody(std::vector<uint8_t>(responseBody, responseBody + sizeof(responseBody)));
|
||||
response->SetBodyStream(std::make_unique<Azure::Core::IO::MemoryBodyStream>(
|
||||
responseBodyStream, sizeof(responseBodyStream) - 1));
|
||||
|
||||
auto exception = Azure::Core::RequestFailedException(response);
|
||||
|
||||
EXPECT_EQ(exception.StatusCode, Azure::Core::Http::HttpStatusCode::ServiceUnavailable);
|
||||
EXPECT_EQ(exception.Message, "JT");
|
||||
EXPECT_EQ(exception.ErrorCode, "503");
|
||||
EXPECT_EQ(exception.RequestId, "1");
|
||||
EXPECT_EQ(exception.ClientRequestId, "2");
|
||||
}
|
||||
|
||||
TEST(RequestFailedException, JSONErrorNoError)
|
||||
{
|
||||
auto response = std::make_unique<Azure::Core::Http::RawResponse>(
|
||||
1, 1, Azure::Core::Http::HttpStatusCode::ServiceUnavailable, "retry please :");
|
||||
static constexpr uint8_t const responseBody[] = "{\"text\" :\"some text\"}";
|
||||
static constexpr uint8_t const responseBodyStream[] = "{\"text\" :\"some text\"}";
|
||||
|
||||
response->SetHeader(HttpShared::ContentType, "application/json");
|
||||
response->SetHeader(HttpShared::MsRequestId, "1");
|
||||
response->SetHeader(HttpShared::MsClientRequestId, "2");
|
||||
response->SetBody(std::vector<uint8_t>(responseBody, responseBody + sizeof(responseBody)));
|
||||
response->SetBodyStream(std::make_unique<Azure::Core::IO::MemoryBodyStream>(
|
||||
responseBodyStream, sizeof(responseBodyStream) - 1));
|
||||
|
||||
auto exception = Azure::Core::RequestFailedException(response);
|
||||
|
||||
EXPECT_EQ(exception.StatusCode, Azure::Core::Http::HttpStatusCode::ServiceUnavailable);
|
||||
EXPECT_EQ(exception.Message, "{\"text\" :\"some text\"}");
|
||||
EXPECT_EQ(exception.ErrorCode, "");
|
||||
EXPECT_EQ(exception.RequestId, "1");
|
||||
EXPECT_EQ(exception.ClientRequestId, "2");
|
||||
}
|
||||
|
||||
TEST(RequestFailedException, NonJSONError)
|
||||
{
|
||||
auto response = std::make_unique<Azure::Core::Http::RawResponse>(
|
||||
1, 1, Azure::Core::Http::HttpStatusCode::ServiceUnavailable, "reason");
|
||||
static constexpr uint8_t const responseBody[] = "NJT";
|
||||
static constexpr uint8_t const responseBodyStream[] = "NJT";
|
||||
|
||||
response->SetHeader(HttpShared::ContentType, "application/text");
|
||||
response->SetHeader(HttpShared::MsRequestId, "1");
|
||||
response->SetHeader(HttpShared::MsClientRequestId, "2");
|
||||
response->SetBody(std::vector<uint8_t>(responseBody, responseBody + sizeof(responseBody)));
|
||||
response->SetBodyStream(std::make_unique<Azure::Core::IO::MemoryBodyStream>(
|
||||
responseBodyStream, sizeof(responseBodyStream) - 1));
|
||||
|
||||
auto exception = Azure::Core::RequestFailedException(response);
|
||||
|
||||
EXPECT_EQ(exception.StatusCode, Azure::Core::Http::HttpStatusCode::ServiceUnavailable);
|
||||
EXPECT_EQ(exception.Message, "NJT");
|
||||
EXPECT_EQ(exception.RequestId, "1");
|
||||
EXPECT_EQ(exception.ClientRequestId, "2");
|
||||
}
|
||||
@ -31,7 +31,6 @@ set(
|
||||
inc/azure/keyvault/common/internal/base64url.hpp
|
||||
inc/azure/keyvault/common/internal/keyvault_pipeline.hpp
|
||||
inc/azure/keyvault/common/internal/single_page.hpp
|
||||
inc/azure/keyvault/common/internal/keyvault_exception.hpp
|
||||
inc/azure/keyvault/common/internal/sha.hpp
|
||||
)
|
||||
|
||||
@ -39,7 +38,7 @@ set(
|
||||
AZURE_KEYVAULT_COMMON_SOURCE
|
||||
src/private/keyvault_constants.hpp
|
||||
src/private/package_version.hpp
|
||||
src/keyvault_exception.cpp
|
||||
|
||||
src/keyvault_pipeline.cpp
|
||||
src/sha.cpp
|
||||
)
|
||||
|
||||
@ -1,34 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
/**
|
||||
* @brief Defines a general exception for Key Vault service clients.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <azure/core/exception.hpp>
|
||||
#include <azure/core/http/http.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
namespace Azure { namespace Security { namespace KeyVault { namespace _internal {
|
||||
/**
|
||||
* @brief Container for static methods to parse Key Vault payloads to Azure Core Exception.
|
||||
*
|
||||
*/
|
||||
struct KeyVaultException final
|
||||
{
|
||||
/**
|
||||
* @brief Parsed the HTTP payload into an #Azure::Core::RequestFailedException
|
||||
*
|
||||
* @param rawResponse The HTTP raw response.
|
||||
* @return Azure::Core::RequestFailedException
|
||||
*/
|
||||
static Azure::Core::RequestFailedException CreateException(
|
||||
std::unique_ptr<Azure::Core::Http::RawResponse> rawResponse);
|
||||
};
|
||||
}}}} // namespace Azure::Security::KeyVault::_internal
|
||||
@ -1,38 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include "azure/keyvault/common/internal/keyvault_exception.hpp"
|
||||
#include "private/keyvault_constants.hpp"
|
||||
|
||||
#include <azure/core/http/policies/policy.hpp>
|
||||
|
||||
#include <azure/core/internal/json/json.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
using namespace Azure::Security::KeyVault;
|
||||
using namespace Azure::Core::Http::_internal;
|
||||
|
||||
Azure::Core::RequestFailedException _internal::KeyVaultException::CreateException(
|
||||
std::unique_ptr<Azure::Core::Http::RawResponse> rawResponse)
|
||||
{
|
||||
std::vector<uint8_t> bodyBuffer = std::move(rawResponse->GetBody());
|
||||
auto& headers = rawResponse->GetHeaders();
|
||||
std::string contentType = HttpShared::GetHeaderOrEmptyString(headers, HttpShared::ContentType);
|
||||
std::string message;
|
||||
std::string errorCode;
|
||||
|
||||
if (contentType.find("json") != std::string::npos)
|
||||
{
|
||||
auto jsonParser = Azure::Core::Json::_internal::json::parse(bodyBuffer);
|
||||
auto& error = jsonParser["error"];
|
||||
errorCode = error["code"].get<std::string>();
|
||||
message = error["message"].get<std::string>();
|
||||
}
|
||||
else
|
||||
{
|
||||
message = std::string(bodyBuffer.begin(), bodyBuffer.end());
|
||||
}
|
||||
Azure::Core::RequestFailedException exception(message, std::move(rawResponse));
|
||||
exception.ErrorCode = std::move(errorCode);
|
||||
return exception;
|
||||
}
|
||||
@ -1,12 +1,12 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <azure/core/http/http.hpp>
|
||||
|
||||
#include "azure/keyvault/common/internal/keyvault_exception.hpp"
|
||||
#include "azure/keyvault/common/internal/keyvault_pipeline.hpp"
|
||||
#include "private/keyvault_constants.hpp"
|
||||
|
||||
#include <azure/core/exception.hpp>
|
||||
#include <azure/core/http/http.hpp>
|
||||
|
||||
using namespace Azure::Security::KeyVault;
|
||||
using namespace Azure::Core::Http::_internal;
|
||||
|
||||
@ -70,8 +70,7 @@ std::unique_ptr<Azure::Core::Http::RawResponse> _internal::KeyVaultPipeline::Sen
|
||||
case Azure::Core::Http::HttpStatusCode::NoContent:
|
||||
break;
|
||||
default:
|
||||
throw Azure::Security::KeyVault::_internal::KeyVaultException::CreateException(
|
||||
std::move(response));
|
||||
throw Azure::Core::RequestFailedException(response);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
|
||||
#include <azure/core/exception.hpp>
|
||||
|
||||
#include <azure/keyvault/common/internal/keyvault_exception.hpp>
|
||||
#include <azure/core/exception.hpp>
|
||||
|
||||
#include "azure/keyvault/keys/delete_key_operation.hpp"
|
||||
#include "azure/keyvault/keys/key_client.hpp"
|
||||
@ -43,8 +43,7 @@ Azure::Security::KeyVault::Keys::DeleteKeyOperation::PollInternal(
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw Azure::Security::KeyVault::_internal::KeyVaultException::CreateException(
|
||||
std::move(rawResponse));
|
||||
throw Azure::Core::RequestFailedException(rawResponse);
|
||||
}
|
||||
|
||||
if (m_status == Azure::Core::OperationStatus::Succeeded)
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <azure/keyvault/common/internal/keyvault_exception.hpp>
|
||||
#include <azure/core/exception.hpp>
|
||||
|
||||
#include "azure/keyvault/keys/key_client.hpp"
|
||||
#include "azure/keyvault/keys/recover_deleted_key_operation.hpp"
|
||||
@ -40,8 +40,7 @@ Azure::Security::KeyVault::Keys::RecoverDeletedKeyOperation::PollInternal(
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw Azure::Security::KeyVault::_internal::KeyVaultException::CreateException(
|
||||
std::move(rawResponse));
|
||||
throw Azure::Core::RequestFailedException(rawResponse);
|
||||
}
|
||||
if (m_status == Azure::Core::OperationStatus::Succeeded)
|
||||
{
|
||||
|
||||
Loading…
Reference in New Issue
Block a user