blob: c1b564fafe9bcae90bb201a8289e0ce0db8d2aa3 [file] [log] [blame]
#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