| #include "tlbmc/redfish/expand_query_aggregator.h" |
| |
| #include <algorithm> |
| #include <array> |
| #include <cstddef> |
| #include <limits> |
| #include <memory> |
| #include <string> |
| #include <system_error> |
| #include <utility> |
| #include <vector> |
| |
| #include "absl/log/log.h" |
| #include "absl/strings/string_view.h" |
| #include "absl/strings/substitute.h" |
| #include "boost/url/url.hpp" // NOLINT |
| #include "nlohmann/json_fwd.hpp" |
| #include "tlbmc/redfish/query_parameters.h" |
| #include "dbus_utility.hpp" // NOLINT |
| #include "async_resp.hpp" // NOLINT |
| #include "http_request.hpp" // NOLINT |
| |
| namespace milotic_tlbmc { |
| |
| using ::crow::Request; |
| using ::crow::Response; |
| |
| std::string ExpandQueryAggregator::CreateExpandQueryUrl( |
| absl::string_view url, [[maybe_unused]] const ExpandNode& expand_node) { |
| return absl::Substitute( |
| "$0?$$expand=$1($$levels=$2)", url, |
| ExpandTypeToUrlParam(query_parameters_.GetExpandType()), |
| std::to_string(query_parameters_.GetExpandLevel() - 1)); |
| } |
| |
| void ExpandQueryAggregator::StartQuery() { |
| std::vector<ExpandNode> urls_to_expand = GetUnexpandedUrls( |
| final_async_resp_->res.jsonValue, query_parameters_.GetExpandType()); |
| for (const ExpandNode& url_to_expand : urls_to_expand) { |
| boost::urls::url expand_url(url_to_expand.url); |
| boost::beast::http::request<boost::beast::http::string_body> request; |
| std::string request_url = |
| CreateExpandQueryUrl(expand_url.encoded_path(), url_to_expand); |
| request.target(request_url); |
| request.method(boost::beast::http::verb::get); |
| |
| std::error_code ec; |
| Request sub_req(std::move(request), ec); |
| auto sub_async_resp = |
| std::make_shared<bmcweb::AsyncResp>(final_async_resp_->strand_); |
| sub_async_resp->res.setCompleteRequestHandler( |
| // We must extend the lifetime of the final_response to the lifetime |
| // of the sub_async_resp. |
| [expand_aggregator = shared_from_this(), url_to_expand](Response& res) { |
| expand_aggregator->AddResponseToFinalResponse( |
| url_to_expand.json_pointer, res); |
| }); |
| LOG(INFO) << "Sub request: " << sub_req.target(); |
| smart_router_->Handle(sub_req, sub_async_resp); |
| } |
| } |
| |
| void ExpandQueryAggregator::AddResponseToFinalResponse( |
| const nlohmann::json::json_pointer& location, Response& sub_response) { |
| PropogateErrorCodeIfNeeded(location, sub_response); |
| if (!sub_response.jsonValue.is_object() || sub_response.jsonValue.empty()) { |
| return; |
| } |
| |
| final_async_resp_->res.jsonValue[location] = |
| std::move(sub_response.jsonValue); |
| } |
| |
| // Make error codes distinct between the query made. |
| void ExpandQueryAggregator::PropogateErrorCodeIfNeeded( |
| const nlohmann::json::json_pointer& location, Response& sub_response) { |
| if (sub_response.resultInt() >= 200 && sub_response.resultInt() < 400) { |
| return; |
| } |
| |
| final_async_resp_->res.result( |
| DetermineFinalErrorCode(sub_response.resultInt())); |
| |
| if (sub_response.jsonValue.contains("error")) { |
| if (!final_async_resp_->res.jsonValue.contains("error")) { |
| final_async_resp_->res.jsonValue["error"] = nlohmann::json::object(); |
| } |
| final_async_resp_->res.jsonValue["error"][location] = |
| std::move(sub_response.jsonValue["error"]); |
| sub_response.jsonValue.clear(); |
| } |
| } |
| |
| // Propogates 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 explictly) |
| // 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 |
| unsigned ExpandQueryAggregator::DetermineFinalErrorCode( |
| unsigned sub_response_code) { |
| unsigned cur_code = final_async_resp_->res.resultInt(); |
| // We keep a explicit list for error codes that this project often uses |
| // Higer priority codes are in lower indexes |
| constexpr std::array<unsigned, 13> ordered_codes = { |
| 500, 507, 503, 502, 501, 401, 412, 409, 406, 405, 404, 403, 400}; |
| size_t final_code_index = std::numeric_limits<size_t>::max(); |
| size_t sub_response_code_index = std::numeric_limits<size_t>::max(); |
| for (size_t i = 0; i < ordered_codes.size(); ++i) { |
| if (ordered_codes[i] == cur_code) { |
| final_code_index = i; |
| } |
| if (ordered_codes[i] == sub_response_code) { |
| sub_response_code_index = i; |
| } |
| } |
| if (final_code_index != std::numeric_limits<size_t>::max() && |
| sub_response_code_index != std::numeric_limits<size_t>::max()) { |
| return final_code_index <= sub_response_code_index ? cur_code |
| : sub_response_code; |
| } |
| if (sub_response_code == 500 || cur_code == 500) { |
| return 500; |
| } |
| if (sub_response_code > 500 || cur_code > 500) { |
| return std::max(cur_code, sub_response_code); |
| } |
| if (sub_response_code == 401) { |
| return sub_response_code; |
| } |
| return std::max(cur_code, sub_response_code); |
| } |
| |
| } // namespace milotic_tlbmc |