blob: 1edaecc69af490d9ce42c868b3c0767a56b02112 [file] [log] [blame]
#pragma once
#include "app.hpp"
#include "app_singleton.hpp"
#include "async_resp.hpp"
#include "http_request.hpp"
#include "http_response.hpp"
#include "logging.hpp"
#include <boost/beast/http/verb.hpp>
#include <boost/url/params_view.hpp>
#include <boost/url/url_view.hpp>
#include <functional>
#include <memory>
#include <new>
#include <optional>
#include <string>
#include <string_view>
#include <type_traits>
#include <utility>
// IWYU pragma: no_forward_declare crow::App
// IWYU pragma: no_include <boost/url/impl/params_view.hpp>
// IWYU pragma: no_include <boost/url/impl/url_view.hpp>
namespace redfish
{
namespace query_param
{
enum class ExpandType : uint8_t
{
None,
Links,
NotLinks,
Both,
};
// A simple implementation of Trie to help |recursiveSelect|.
class SelectTrieNode
{
public:
SelectTrieNode() = default;
const SelectTrieNode* find(const std::string& jsonKey) const
{
auto it = children.find(jsonKey);
if (it == children.end())
{
return nullptr;
}
return &it->second;
}
// Creates a new node if the key doesn't exist, returns the reference to the
// newly created node; otherwise, return the reference to the existing node
SelectTrieNode* emplace(std::string_view jsonKey)
{
auto [it, _] = children.emplace(jsonKey, SelectTrieNode{});
return &it->second;
}
bool empty() const
{
return children.empty();
}
void clear()
{
children.clear();
}
void setToSelected()
{
selected = true;
}
bool isSelected() const
{
return selected;
}
private:
std::map<std::string, SelectTrieNode, std::less<>> children;
bool selected = false;
};
struct SelectTrie
{
SelectTrie() = default;
// Inserts a $select value; returns false if the nestedProperty is illegal.
bool insertNode(std::string_view nestedProperty);
SelectTrieNode root;
};
// The struct stores the parsed query parameters of the default Redfish route.
struct Query
{
// Only
bool isOnly = false;
// Expand
uint8_t expandLevel = 0;
ExpandType expandType = ExpandType::None;
// Skip
std::optional<size_t> skip = std::nullopt;
// Top
static constexpr size_t maxTop = 1000; // Max entries a response contain
std::optional<size_t> top = std::nullopt;
// Select
SelectTrie selectTrie = {}; // NOLINT
// Filter
// We only support delegate filter for now.
// Unless explicitly enable canDelegateFilter capability, common queries
// won't parse $filter in the query.
std::string filter = ""; // NOLINT
};
// The struct defines how resource handlers in redfish-core/lib/ can handle
// query parameters themselves, so that the default Redfish route will delegate
// the processing.
struct QueryCapabilities
{
bool canDelegateOnly = false;
bool canDelegateTop = false;
bool canDelegateSkip = false;
uint8_t canDelegateExpandLevel = 0;
bool canDelegateSelect = false;
bool canDelegateFilter = false;
bool canHandleSubscription = false;
};
struct ExpandNode
{
nlohmann::json::json_pointer location;
std::string uri;
inline bool operator==(const ExpandNode& other) const
{
return location == other.location && uri == other.uri;
}
};
Query delegate(const QueryCapabilities& queryCapabilities, Query& query,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp);
std::optional<std::string> formatQueryForExpand(const Query& query);
bool isSelectedPropertyAllowed(std::string_view property);
bool getSelectParam(std::string_view value, Query& query);
void recursiveSelect(nlohmann::json& currRoot, const SelectTrieNode& currNode);
unsigned propogateErrorCode(unsigned finalCode, unsigned subResponseCode);
void propogateError(crow::Response& finalResponse, crow::Response& subResponse);
std::optional<Query> parseParameters(boost::urls::params_view urlParams,
crow::Response& res);
bool getExpandType(std::string_view value, Query& query);
std::vector<ExpandNode> findNavigationReferences(ExpandType eType, int depth,
nlohmann::json& jsonResponse);
} // namespace query_param
// Sets up the Redfish Route and delegates some of the query parameter
// processing. |queryCapabilities| stores which query parameters will be
// handled by redfish-core/lib codes, then default query parameter handler won't
// process these parameters.
[[nodiscard]] bool setUpRedfishRouteWithDelegation(
crow::App& app, const crow::Request& req,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
query_param::Query& delegated,
const query_param::QueryCapabilities& queryCapabilities);
// Sets up the Redfish Route. All parameters are handled by the default handler.
[[nodiscard]] inline bool
setUpRedfishRoute(crow::App& app, const crow::Request& req,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
{
// This route |delegated| is never used
query_param::Query delegated;
return setUpRedfishRouteWithDelegation(app, req, asyncResp, delegated,
query_param::QueryCapabilities{});
}
// See |setUpRedfishRouteWithDelegation| above for documentation.
[[nodiscard]] inline bool setUpRedfishRouteWithDelegationOnGlobalApp(
const crow::Request& req,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
query_param::Query& delegated,
const query_param::QueryCapabilities& queryCapabilities)
{
return setUpRedfishRouteWithDelegation(
*crow::globalBmcWebApp, req, asyncResp, delegated, queryCapabilities);
}
// See |setUpRedfishRoute| above for documentation.
[[nodiscard]] inline bool
setUpRedfishRouteOnGlobalApp(const crow::Request& req,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
{
return setUpRedfishRoute(*crow::globalBmcWebApp, req, asyncResp);
}
} // namespace redfish