blob: 951873d0a093ddfd946d79faea4a463f6c3af5af [file] [log] [blame]
#ifndef THIRD_PARTY_GBMCWEB_REDFISH_CORE_INCLUDE_QUERY_H_
#define THIRD_PARTY_GBMCWEB_REDFISH_CORE_INCLUDE_QUERY_H_
#include <cstddef>
#include <cstdint>
#include <functional>
#include <map>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#include "boost/beast/http/verb.hpp" // NOLINT
#include "boost/url/params_view.hpp" // NOLINT
#include "boost/url/url_view.hpp" // NOLINT
#include "app.hpp"
#include "app_singleton.hpp"
#include "http_request.hpp"
#include "http_response.hpp"
#include "async_resp.hpp"
#include <nlohmann/json.hpp>
// 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
#endif // THIRD_PARTY_GBMCWEB_REDFISH_CORE_INCLUDE_QUERY_H_