blob: b37435e17cb3bc44a010cd1bc8f693d170109311 [file] [log] [blame]
#include "tlbmc/redfish/response.h"
#include <string_view>
#include "absl/status/status.h"
#include "absl/strings/str_cat.h"
#include "redfish_v1.pb.h"
#include "grpcpp/support/server_callback.h"
#include "grpcpp/support/status.h"
#include "nlohmann/json.hpp"
namespace milotic_tlbmc {
RedfishResponse::RedfishResponse(grpc::ServerUnaryReactor* reactor,
redfish::v1::Response& grpc_response)
: reactor_(reactor), grpc_response_(&grpc_response) {}
void RedfishResponse::End() {
// Don't send response if end has already been called.
if (end_called_) {
return;
}
end_called_ = true;
// If there is an async resp attached, then we will use it to send the
// response back instead of the reactor.
if (async_resp_ != nullptr) {
for (const auto& element : http_response_.base()) {
async_resp_->res.addHeader(element.name_string(), element.value());
}
async_resp_->res.result(http_response_.result());
async_resp_->res.body() = http_response_.body();
async_resp_->res.jsonValue = json_body_;
return;
}
// If there is no grpc response, then we don't need to send the response.
if (grpc_response_ == nullptr) {
return;
}
auto* headers = grpc_response_->mutable_headers();
for (const auto& element : http_response_.base()) {
// Repeated headers are combined into a ',' separated list. See
// https://www.rfc-editor.org/rfc/rfc9110.html#section-5.3-3.
auto found = headers->find(element.name_string());
if (found == headers->end()) {
(*headers)[element.name_string()] = element.value();
} else {
found->second += ",";
found->second += element.value();
}
}
if (http_response_["OData-Version"] == "4.0") {
*grpc_response_->mutable_json_str() = json_body_.dump(
-1, ' ', true, nlohmann::json::error_handler_t::replace);
} else {
*grpc_response_->mutable_octet_stream() = http_response_.body();
}
grpc_response_->set_code(static_cast<unsigned int>(http_response_.result()));
if (reactor_ != nullptr) {
reactor_->Finish(grpc::Status::OK);
}
}
void RedfishResponse::SetBodyToJson() {
http_response_.base().erase("OData-Version");
SetHeader("OData-Version", "4.0");
}
void RedfishResponse::SetBodyToOctetStream() {
http_response_.base().erase("OData-Version");
}
void RedfishResponse::SetHeader(std::string_view key, std::string_view value) {
http_response_.base().insert(key, value);
}
void RedfishResponse::SetBinaryBody(std::string_view body) {
http_response_.body() = body;
}
void RedfishResponse::SetToNotFound(std::string_view error_message) {
http_response_.result(boost::beast::http::status::not_found);
nlohmann::json error_message_json;
error_message_json["@odata.type"] = "#Message.v1_1_1.Message";
error_message_json["MessageId"] = "Base.1.13.0.ResourceNotFound";
error_message_json["Message"] = error_message;
SetErrorMessage(error_message_json);
}
void RedfishResponse::SetToForbidden(std::string_view error_message) {
http_response_.result(boost::beast::http::status::forbidden);
nlohmann::json error_message_json;
error_message_json["@odata.type"] = "#Message.v1_1_1.Message";
error_message_json["MessageId"] = "Base.1.13.0.ResourceAtUriUnauthorized";
error_message_json["Message"] = error_message;
SetErrorMessage(error_message_json);
}
void RedfishResponse::SetToNotReady(std::string_view error_message) {
http_response_.result(boost::beast::http::status::service_unavailable);
nlohmann::json error_message_json;
error_message_json["@odata.type"] = "#Message.v1_1_1.Message";
error_message_json["MessageId"] = "Base.1.13.0.InternalError";
error_message_json["Message"] = error_message;
SetErrorMessage(error_message_json);
}
void RedfishResponse::SetToAbslStatus(const absl::Status& status) {
switch (status.code()) {
case absl::StatusCode::kNotFound:
SetToNotFound(status.message());
break;
case absl::StatusCode::kPermissionDenied:
SetToForbidden(status.message());
break;
case absl::StatusCode::kUnavailable:
SetToNotReady(status.message());
break;
default:
http_response_.result(boost::beast::http::status::internal_server_error);
break;
}
}
void RedfishResponse::SetErrorMessage(
const nlohmann::json& error_message_json) {
if (!json_body_.contains("/error")) {
SetKeyInJsonBody(/*key=*/"/error/code", /*value=*/"Base.1.8.GeneralError");
SetKeyInJsonBody(/*key=*/"/error/message",
/*value=*/
"A general error has occurred. See Resolution for "
"information on how to "
"resolve the error.");
}
AppendToKeyInJsonBody(/*key=*/"/error/@Message.ExtendedInfo",
/*value=*/error_message_json);
}
} // namespace milotic_tlbmc