Move error code utils into new file
Error code utils can be used for aggregating error
responses from multiples responses including use case
for managing separate route handlers for OEM namespace
Tests
Units tests are passing.
Change-Id: I2223d41fb318c0276de1ca64dd3e841bb988d902
Signed-off-by: rohitpai <rohitpai77@gmail.com>
diff --git a/meson.build b/meson.build
index 9c16bde..f00f05d 100644
--- a/meson.build
+++ b/meson.build
@@ -452,6 +452,7 @@
'test/redfish-core/include/utils/ip_utils_test.cpp',
'test/redfish-core/include/utils/json_utils_test.cpp',
'test/redfish-core/include/utils/query_param_test.cpp',
+ 'test/redfish-core/include/utils/error_code_test.cpp',
'test/redfish-core/include/utils/sensor_utils_test.cpp',
'test/redfish-core/include/utils/stl_utils_test.cpp',
'test/redfish-core/include/utils/time_utils_test.cpp',
diff --git a/redfish-core/include/error_message_utils.hpp b/redfish-core/include/error_message_utils.hpp
index 75015bc..c4af953 100644
--- a/redfish-core/include/error_message_utils.hpp
+++ b/redfish-core/include/error_message_utils.hpp
@@ -1,3 +1,4 @@
+#pragma once
#include <nlohmann/json.hpp>
#include <string_view>
diff --git a/redfish-core/include/utils/error_code.hpp b/redfish-core/include/utils/error_code.hpp
new file mode 100644
index 0000000..6cb32d7
--- /dev/null
+++ b/redfish-core/include/utils/error_code.hpp
@@ -0,0 +1,94 @@
+// SPDX-License-Identifier: Apache-2.0
+// SPDX-FileCopyrightText: Copyright OpenBMC Authors
+#pragma once
+
+#include "error_message_utils.hpp"
+#include "http_response.hpp"
+
+#include <algorithm>
+#include <array>
+#include <cstddef>
+#include <limits>
+
+namespace redfish
+{
+// Propagates the worst error code to the final response.
+// The order of error code is (from high to low)
+// 500 Internal Server Error
+// 511 Network Authentication Required
+// 510 Not Extended
+// 508 Loop Detected
+// 507 Insufficient Storage
+// 506 Variant Also Negotiates
+// 505 HTTP Version Not Supported
+// 504 Gateway Timeout
+// 503 Service Unavailable
+// 502 Bad Gateway
+// 501 Not Implemented
+// 401 Unauthorized
+// 451 - 409 Error codes (not listed explicitly)
+// 408 Request Timeout
+// 407 Proxy Authentication Required
+// 406 Not Acceptable
+// 405 Method Not Allowed
+// 404 Not Found
+// 403 Forbidden
+// 402 Payment Required
+// 400 Bad Request
+inline unsigned propogateErrorCode(unsigned finalCode, unsigned subResponseCode)
+{
+ // We keep a explicit list for error codes that this project often uses
+ // Higher priority codes are in lower indexes
+ constexpr std::array<unsigned, 13> orderedCodes = {
+ 500, 507, 503, 502, 501, 401, 412, 409, 406, 405, 404, 403, 400};
+ size_t finalCodeIndex = std::numeric_limits<size_t>::max();
+ size_t subResponseCodeIndex = std::numeric_limits<size_t>::max();
+ for (size_t i = 0; i < orderedCodes.size(); ++i)
+ {
+ if (orderedCodes[i] == finalCode)
+ {
+ finalCodeIndex = i;
+ }
+ if (orderedCodes[i] == subResponseCode)
+ {
+ subResponseCodeIndex = i;
+ }
+ }
+ if (finalCodeIndex != std::numeric_limits<size_t>::max() &&
+ subResponseCodeIndex != std::numeric_limits<size_t>::max())
+ {
+ return finalCodeIndex <= subResponseCodeIndex
+ ? finalCode
+ : subResponseCode;
+ }
+ if (subResponseCode == 500 || finalCode == 500)
+ {
+ return 500;
+ }
+ if (subResponseCode > 500 || finalCode > 500)
+ {
+ return std::max(finalCode, subResponseCode);
+ }
+ if (subResponseCode == 401)
+ {
+ return subResponseCode;
+ }
+ return std::max(finalCode, subResponseCode);
+}
+
+// Propagates all error messages into |finalResponse|
+inline void propogateError(crow::Response& finalResponse,
+ crow::Response& subResponse)
+{
+ // no errors
+ if (subResponse.resultInt() >= 200 && subResponse.resultInt() < 400)
+ {
+ return;
+ }
+ messages::moveErrorsToErrorJson(finalResponse.jsonValue,
+ subResponse.jsonValue);
+ finalResponse.result(
+ propogateErrorCode(finalResponse.resultInt(), subResponse.resultInt()));
+}
+
+} // namespace redfish
diff --git a/redfish-core/include/utils/query_param.hpp b/redfish-core/include/utils/query_param.hpp
index 529b79d..107f659 100644
--- a/redfish-core/include/utils/query_param.hpp
+++ b/redfish-core/include/utils/query_param.hpp
@@ -5,7 +5,7 @@
#include "app.hpp"
#include "async_resp.hpp"
-#include "error_message_utils.hpp"
+#include "error_code.hpp"
#include "error_messages.hpp"
#include "filter_expr_executor.hpp"
#include "filter_expr_parser_ast.hpp"
@@ -739,85 +739,6 @@
return str;
}
-// Propagates the worst error code to the final response.
-// The order of error code is (from high to low)
-// 500 Internal Server Error
-// 511 Network Authentication Required
-// 510 Not Extended
-// 508 Loop Detected
-// 507 Insufficient Storage
-// 506 Variant Also Negotiates
-// 505 HTTP Version Not Supported
-// 504 Gateway Timeout
-// 503 Service Unavailable
-// 502 Bad Gateway
-// 501 Not Implemented
-// 401 Unauthorized
-// 451 - 409 Error codes (not listed explicitly)
-// 408 Request Timeout
-// 407 Proxy Authentication Required
-// 406 Not Acceptable
-// 405 Method Not Allowed
-// 404 Not Found
-// 403 Forbidden
-// 402 Payment Required
-// 400 Bad Request
-inline unsigned propogateErrorCode(unsigned finalCode, unsigned subResponseCode)
-{
- // We keep a explicit list for error codes that this project often uses
- // Higher priority codes are in lower indexes
- constexpr std::array<unsigned, 13> orderedCodes = {
- 500, 507, 503, 502, 501, 401, 412, 409, 406, 405, 404, 403, 400};
- size_t finalCodeIndex = std::numeric_limits<size_t>::max();
- size_t subResponseCodeIndex = std::numeric_limits<size_t>::max();
- for (size_t i = 0; i < orderedCodes.size(); ++i)
- {
- if (orderedCodes[i] == finalCode)
- {
- finalCodeIndex = i;
- }
- if (orderedCodes[i] == subResponseCode)
- {
- subResponseCodeIndex = i;
- }
- }
- if (finalCodeIndex != std::numeric_limits<size_t>::max() &&
- subResponseCodeIndex != std::numeric_limits<size_t>::max())
- {
- return finalCodeIndex <= subResponseCodeIndex
- ? finalCode
- : subResponseCode;
- }
- if (subResponseCode == 500 || finalCode == 500)
- {
- return 500;
- }
- if (subResponseCode > 500 || finalCode > 500)
- {
- return std::max(finalCode, subResponseCode);
- }
- if (subResponseCode == 401)
- {
- return subResponseCode;
- }
- return std::max(finalCode, subResponseCode);
-}
-
-// Propagates all error messages into |finalResponse|
-inline void propogateError(crow::Response& finalResponse,
- crow::Response& subResponse)
-{
- // no errors
- if (subResponse.resultInt() >= 200 && subResponse.resultInt() < 400)
- {
- return;
- }
- messages::moveErrorsToErrorJson(finalResponse.jsonValue,
- subResponse.jsonValue);
- finalResponse.result(
- propogateErrorCode(finalResponse.resultInt(), subResponse.resultInt()));
-}
-
class MultiAsyncResp : public std::enable_shared_from_this<MultiAsyncResp>
{
public:
diff --git a/test/redfish-core/include/utils/error_code_test.cpp b/test/redfish-core/include/utils/error_code_test.cpp
new file mode 100644
index 0000000..60c09ac
--- /dev/null
+++ b/test/redfish-core/include/utils/error_code_test.cpp
@@ -0,0 +1,161 @@
+// SPDX-License-Identifier: Apache-2.0
+// SPDX-FileCopyrightText: Copyright OpenBMC Authors
+#include "error_message_utils.hpp"
+#include "http_response.hpp"
+#include "utils/error_code.hpp"
+
+#include <boost/beast/http/status.hpp>
+#include <nlohmann/json.hpp>
+
+#include <array>
+#include <string>
+
+#include <gtest/gtest.h>
+
+namespace redfish::error_code
+{
+namespace
+{
+
+TEST(PropogateErrorCode, 500IsWorst)
+{
+ constexpr std::array<unsigned, 7> codes = {100, 200, 300, 400,
+ 401, 500, 501};
+ for (auto code : codes)
+ {
+ EXPECT_EQ(propogateErrorCode(500, code), 500);
+ EXPECT_EQ(propogateErrorCode(code, 500), 500);
+ }
+}
+
+TEST(PropogateErrorCode, 5xxAreWorseThanOthers)
+{
+ constexpr std::array<unsigned, 7> codes = {100, 200, 300, 400,
+ 401, 501, 502};
+ for (auto code : codes)
+ {
+ EXPECT_EQ(propogateErrorCode(code, 505), 505);
+ EXPECT_EQ(propogateErrorCode(505, code), 505);
+ }
+ EXPECT_EQ(propogateErrorCode(502, 501), 502);
+ EXPECT_EQ(propogateErrorCode(501, 502), 502);
+ EXPECT_EQ(propogateErrorCode(503, 502), 503);
+}
+
+TEST(PropogateErrorCode, 401IsWorseThanOthers)
+{
+ constexpr std::array<unsigned, 7> codes = {100, 200, 300, 400, 401};
+ for (auto code : codes)
+ {
+ EXPECT_EQ(propogateErrorCode(code, 401), 401);
+ EXPECT_EQ(propogateErrorCode(401, code), 401);
+ }
+}
+
+TEST(PropogateErrorCode, 4xxIsWorseThanOthers)
+{
+ constexpr std::array<unsigned, 7> codes = {100, 200, 300, 400, 402};
+ for (auto code : codes)
+ {
+ EXPECT_EQ(propogateErrorCode(code, 405), 405);
+ EXPECT_EQ(propogateErrorCode(405, code), 405);
+ }
+ EXPECT_EQ(propogateErrorCode(400, 402), 402);
+ EXPECT_EQ(propogateErrorCode(402, 403), 403);
+ EXPECT_EQ(propogateErrorCode(403, 402), 403);
+}
+
+TEST(PropogateError, IntermediateNoErrorMessageMakesNoChange)
+{
+ crow::Response intermediate;
+ intermediate.result(boost::beast::http::status::ok);
+
+ crow::Response finalRes;
+ finalRes.result(boost::beast::http::status::ok);
+ propogateError(finalRes, intermediate);
+ EXPECT_EQ(finalRes.result(), boost::beast::http::status::ok);
+ EXPECT_EQ(finalRes.jsonValue.find("error"), finalRes.jsonValue.end());
+}
+
+TEST(PropogateError, ErrorsArePropergatedWithErrorInRoot)
+{
+ nlohmann::json root = R"(
+{
+ "@odata.type": "#Message.v1_1_1.Message",
+ "Message": "The request failed due to an internal service error. The service is still operational.",
+ "MessageArgs": [],
+ "MessageId": "Base.1.13.0.InternalError",
+ "MessageSeverity": "Critical",
+ "Resolution": "Resubmit the request. If the problem persists, consider resetting the service."
+}
+)"_json;
+ crow::Response intermediate;
+ intermediate.result(boost::beast::http::status::internal_server_error);
+ intermediate.jsonValue = root;
+
+ crow::Response final;
+ final.result(boost::beast::http::status::ok);
+
+ propogateError(final, intermediate);
+
+ EXPECT_EQ(final.jsonValue["error"]["code"].get<std::string>(),
+ "Base.1.13.0.InternalError");
+ EXPECT_EQ(
+ final.jsonValue["error"]["message"].get<std::string>(),
+ "The request failed due to an internal service error. The service is still operational.");
+ EXPECT_EQ(intermediate.jsonValue, R"({})"_json);
+ EXPECT_EQ(final.result(),
+ boost::beast::http::status::internal_server_error);
+}
+
+TEST(PropogateError, ErrorsArePropergatedWithErrorCode)
+{
+ crow::Response intermediate;
+ intermediate.result(boost::beast::http::status::internal_server_error);
+
+ nlohmann::json error = R"(
+{
+ "error": {
+ "@Message.ExtendedInfo": [],
+ "code": "Base.1.13.0.InternalError",
+ "message": "The request failed due to an internal service error. The service is still operational."
+ }
+}
+)"_json;
+ nlohmann::json extendedInfo = R"(
+{
+ "@odata.type": "#Message.v1_1_1.Message",
+ "Message": "The request failed due to an internal service error. The service is still operational.",
+ "MessageArgs": [],
+ "MessageId": "Base.1.13.0.InternalError",
+ "MessageSeverity": "Critical",
+ "Resolution": "Resubmit the request. If the problem persists, consider resetting the service."
+}
+)"_json;
+
+ for (int i = 0; i < 10; ++i)
+ {
+ error["error"][messages::messageAnnotation].push_back(extendedInfo);
+ }
+ intermediate.jsonValue = error;
+ crow::Response final;
+ final.result(boost::beast::http::status::ok);
+
+ propogateError(final, intermediate);
+ EXPECT_EQ(final.jsonValue["error"][messages::messageAnnotation],
+ error["error"][messages::messageAnnotation]);
+ std::string errorCode = messages::messageVersionPrefix;
+ errorCode += "GeneralError";
+ std::string errorMessage =
+ "A general error has occurred. See Resolution for "
+ "information on how to resolve the error.";
+ EXPECT_EQ(final.jsonValue["error"]["code"].get<std::string>(), errorCode);
+ EXPECT_EQ(final.jsonValue["error"]["message"].get<std::string>(),
+ errorMessage);
+ EXPECT_EQ(intermediate.jsonValue, R"({})"_json);
+ EXPECT_EQ(final.result(),
+ boost::beast::http::status::internal_server_error);
+}
+
+} // namespace
+} // namespace redfish::error_code
diff --git a/test/redfish-core/include/utils/query_param_test.cpp b/test/redfish-core/include/utils/query_param_test.cpp
index d9025c0..44d54bd 100644
--- a/test/redfish-core/include/utils/query_param_test.cpp
+++ b/test/redfish-core/include/utils/query_param_test.cpp
@@ -11,7 +11,6 @@
#include <boost/url/url_view.hpp>
#include <nlohmann/json.hpp>
-#include <array>
#include <optional>
#include <span>
#include <string>
@@ -369,146 +368,6 @@
EXPECT_EQ(root, expected);
}
-TEST(PropogateErrorCode, 500IsWorst)
-{
- constexpr std::array<unsigned, 7> codes = {100, 200, 300, 400,
- 401, 500, 501};
- for (auto code : codes)
- {
- EXPECT_EQ(propogateErrorCode(500, code), 500);
- EXPECT_EQ(propogateErrorCode(code, 500), 500);
- }
-}
-
-TEST(PropogateErrorCode, 5xxAreWorseThanOthers)
-{
- constexpr std::array<unsigned, 7> codes = {100, 200, 300, 400,
- 401, 501, 502};
- for (auto code : codes)
- {
- EXPECT_EQ(propogateErrorCode(code, 505), 505);
- EXPECT_EQ(propogateErrorCode(505, code), 505);
- }
- EXPECT_EQ(propogateErrorCode(502, 501), 502);
- EXPECT_EQ(propogateErrorCode(501, 502), 502);
- EXPECT_EQ(propogateErrorCode(503, 502), 503);
-}
-
-TEST(PropogateErrorCode, 401IsWorseThanOthers)
-{
- constexpr std::array<unsigned, 7> codes = {100, 200, 300, 400, 401};
- for (auto code : codes)
- {
- EXPECT_EQ(propogateErrorCode(code, 401), 401);
- EXPECT_EQ(propogateErrorCode(401, code), 401);
- }
-}
-
-TEST(PropogateErrorCode, 4xxIsWorseThanOthers)
-{
- constexpr std::array<unsigned, 7> codes = {100, 200, 300, 400, 402};
- for (auto code : codes)
- {
- EXPECT_EQ(propogateErrorCode(code, 405), 405);
- EXPECT_EQ(propogateErrorCode(405, code), 405);
- }
- EXPECT_EQ(propogateErrorCode(400, 402), 402);
- EXPECT_EQ(propogateErrorCode(402, 403), 403);
- EXPECT_EQ(propogateErrorCode(403, 402), 403);
-}
-
-TEST(PropogateError, IntermediateNoErrorMessageMakesNoChange)
-{
- crow::Response intermediate;
- intermediate.result(boost::beast::http::status::ok);
-
- crow::Response finalRes;
- finalRes.result(boost::beast::http::status::ok);
- propogateError(finalRes, intermediate);
- EXPECT_EQ(finalRes.result(), boost::beast::http::status::ok);
- EXPECT_EQ(finalRes.jsonValue.find("error"), finalRes.jsonValue.end());
-}
-
-TEST(PropogateError, ErrorsArePropergatedWithErrorInRoot)
-{
- nlohmann::json root = R"(
-{
- "@odata.type": "#Message.v1_1_1.Message",
- "Message": "The request failed due to an internal service error. The service is still operational.",
- "MessageArgs": [],
- "MessageId": "Base.1.13.0.InternalError",
- "MessageSeverity": "Critical",
- "Resolution": "Resubmit the request. If the problem persists, consider resetting the service."
-}
-)"_json;
- crow::Response intermediate;
- intermediate.result(boost::beast::http::status::internal_server_error);
- intermediate.jsonValue = root;
-
- crow::Response final;
- final.result(boost::beast::http::status::ok);
-
- propogateError(final, intermediate);
-
- EXPECT_EQ(final.jsonValue["error"]["code"].get<std::string>(),
- "Base.1.13.0.InternalError");
- EXPECT_EQ(
- final.jsonValue["error"]["message"].get<std::string>(),
- "The request failed due to an internal service error. The service is still operational.");
- EXPECT_EQ(intermediate.jsonValue, R"({})"_json);
- EXPECT_EQ(final.result(),
- boost::beast::http::status::internal_server_error);
-}
-
-TEST(PropogateError, ErrorsArePropergatedWithErrorCode)
-{
- crow::Response intermediate;
- intermediate.result(boost::beast::http::status::internal_server_error);
-
- nlohmann::json error = R"(
-{
- "error": {
- "@Message.ExtendedInfo": [],
- "code": "Base.1.13.0.InternalError",
- "message": "The request failed due to an internal service error. The service is still operational."
- }
-}
-)"_json;
- nlohmann::json extendedInfo = R"(
-{
- "@odata.type": "#Message.v1_1_1.Message",
- "Message": "The request failed due to an internal service error. The service is still operational.",
- "MessageArgs": [],
- "MessageId": "Base.1.13.0.InternalError",
- "MessageSeverity": "Critical",
- "Resolution": "Resubmit the request. If the problem persists, consider resetting the service."
-}
-)"_json;
-
- for (int i = 0; i < 10; ++i)
- {
- error["error"][messages::messageAnnotation].push_back(extendedInfo);
- }
- intermediate.jsonValue = error;
- crow::Response final;
- final.result(boost::beast::http::status::ok);
-
- propogateError(final, intermediate);
- EXPECT_EQ(final.jsonValue["error"][messages::messageAnnotation],
- error["error"][messages::messageAnnotation]);
- std::string errorCode = messages::messageVersionPrefix;
- errorCode += "GeneralError";
- std::string errorMessage =
- "A general error has occurred. See Resolution for "
- "information on how to resolve the error.";
- EXPECT_EQ(final.jsonValue["error"]["code"].get<std::string>(), errorCode);
- EXPECT_EQ(final.jsonValue["error"]["message"].get<std::string>(),
- errorMessage);
- EXPECT_EQ(intermediate.jsonValue, R"({})"_json);
- EXPECT_EQ(final.result(),
- boost::beast::http::status::internal_server_error);
-}
-
TEST(QueryParams, ParseParametersOnly)
{
auto ret = boost::urls::parse_relative_ref("/redfish/v1?only");