summaryrefslogtreecommitdiff
path: root/redfish-core
diff options
context:
space:
mode:
authorEd Tanous <edtanous@google.com>2022-03-23 09:53:51 +0300
committerEd Tanous <ed@tanous.net>2022-04-05 21:50:46 +0300
commit7cf436c913a109c0d3ebf7e696970966500bc6b6 (patch)
tree395401354b8ea67abd0ce20a769af9f584b74a55 /redfish-core
parentf4c99e70dad320abf84fd25a32ad5fce2bf16f4a (diff)
downloadbmcweb-7cf436c913a109c0d3ebf7e696970966500bc6b6.tar.xz
Implement Expand
Section 7.3 of the Redfish specification lays out a feature called "expand" that allows users to expand portions of the Redfish tree automatically on the server side. This commit implements them to the specification. To accomplish this, a new class, MultiAsyncResp is created, that allows RAII objects to handle lifetime properly. When an expand query is generated, a MultiAsyncResp object is instantiated, which allows "new" requests to attach themselves to the multi object, and keep the request alive until they all complete. This also allows requests to be created, while requests are in flight, which is required for queries above depth=1. Negatives: Similar to the previous $only commit, this requires that all nodes redfish nodes now capture App by reference. This is common, but does interfere with some of our other patterns, and attempts to improve the syntactic sugar for this proved unworkable. This commit only adds the above to service root and Computer systems, in hopes that we find a better syntax before this merges. Left to future patches in series: Merging the error json structures in responses. The Redfish spec isn't very clear on how errors propagate for expanded queries, and in a conforming we shouldn't ever hit them, but nonetheless, I suspect the behavior we have is sub-optimal (attaching an error node to every place in the tree that had an issue) and we should attempt to do better in the future. Tested (on previous patch): curl --insecure --user root:0penBmc https://localhost:18080/redfish/v1\?\$expand\=.\(\$levels\=255\) Returns the full tree Setting $levels=1 query returns only a depth of 1 tree being returned. Unit tests passing Signed-off-by: Ed Tanous <edtanous@google.com> Change-Id: I874aabfaa9df5dbf832a80ec62ae65369284791d
Diffstat (limited to 'redfish-core')
-rw-r--r--redfish-core/include/query.hpp8
-rw-r--r--redfish-core/include/utils/query_param.hpp273
-rw-r--r--redfish-core/include/utils/query_param_test.cpp190
-rw-r--r--redfish-core/lib/service_root.hpp31
-rw-r--r--redfish-core/lib/ut/service_root_test.cpp28
-rw-r--r--redfish-core/src/query_param.cpp0
6 files changed, 511 insertions, 19 deletions
diff --git a/redfish-core/include/query.hpp b/redfish-core/include/query.hpp
index 44f07ba048..b06278b472 100644
--- a/redfish-core/include/query.hpp
+++ b/redfish-core/include/query.hpp
@@ -23,13 +23,19 @@ namespace redfish
return false;
}
+ // If this isn't a get, no need to do anything with parameters
+ if (req.method() != boost::beast::http::verb::get)
+ {
+ return true;
+ }
+
std::function<void(crow::Response&)> handler =
res.releaseCompleteRequestHandler();
res.setCompleteRequestHandler(
[&app, handler(std::move(handler)),
query{*queryOpt}](crow::Response& res) mutable {
- processAllParams(app, query, res, handler);
+ processAllParams(app, query, handler, res);
});
return true;
}
diff --git a/redfish-core/include/utils/query_param.hpp b/redfish-core/include/utils/query_param.hpp
index d4ff81f5f3..7aef2f0923 100644
--- a/redfish-core/include/utils/query_param.hpp
+++ b/redfish-core/include/utils/query_param.hpp
@@ -5,8 +5,10 @@
#include "http_request.hpp"
#include "routing.hpp"
+#include <charconv>
#include <string>
#include <string_view>
+#include <utility>
#include <vector>
namespace redfish
@@ -14,11 +16,66 @@ namespace redfish
namespace query_param
{
+enum class ExpandType : uint8_t
+{
+ None,
+ Links,
+ NotLinks,
+ Both,
+};
+
struct Query
{
bool isOnly = false;
+ uint8_t expandLevel = 1;
+ ExpandType expandType = ExpandType::None;
};
+inline bool getExpandType(std::string_view value, Query& query)
+{
+ if (value.empty())
+ {
+ return false;
+ }
+ switch (value[0])
+ {
+ case '*':
+ query.expandType = ExpandType::Both;
+ break;
+ case '.':
+ query.expandType = ExpandType::NotLinks;
+ break;
+ case '~':
+ query.expandType = ExpandType::Links;
+ break;
+ default:
+ return false;
+
+ break;
+ }
+ value.remove_prefix(1);
+ if (value.empty())
+ {
+ query.expandLevel = 1;
+ return true;
+ }
+ constexpr std::string_view levels = "($levels=";
+ if (!value.starts_with(levels))
+ {
+ return false;
+ }
+ value.remove_prefix(levels.size());
+
+ auto it = std::from_chars(value.data(), value.data() + value.size(),
+ query.expandLevel);
+ if (it.ec != std::errc())
+ {
+ return false;
+ }
+ value.remove_prefix(static_cast<size_t>(it.ptr - value.data()));
+ return value == ")";
+}
+
inline std::optional<Query>
parseParameters(const boost::urls::params_view& urlParams,
crow::Response& res)
@@ -37,7 +94,31 @@ inline std::optional<Query>
}
ret.isOnly = true;
}
+ else if (key == "$expand")
+ {
+ if (!getExpandType(value, ret))
+ {
+ messages::queryParameterValueFormatError(res, value, key);
+ return std::nullopt;
+ }
+ }
+ else
+ {
+ // Intentionally ignore other errors Redfish spec, 7.3.1
+ if (key.starts_with("$"))
+ {
+ // Services shall return... The HTTP 501 Not Implemented
+ // status code for any unsupported query parameters that
+ // start with $ .
+ messages::queryParameterValueFormatError(res, value, key);
+ res.result(boost::beast::http::status::not_implemented);
+ return std::nullopt;
+ }
+ // "Shall ignore unknown or unsupported query parameters that do
+ // not begin with $ ."
+ }
}
+
return ret;
}
@@ -96,9 +177,180 @@ inline bool processOnly(crow::App& app, crow::Response& res,
return true;
}
-void processAllParams(crow::App& app, Query query,
- crow::Response& intermediateResponse,
- std::function<void(crow::Response&)>& completionHandler)
+struct ExpandNode
+{
+ nlohmann::json::json_pointer location;
+ std::string uri;
+
+ inline bool operator==(const ExpandNode& other) const
+ {
+ return location == other.location && uri == other.uri;
+ }
+};
+
+// Walks a json object looking for Redfish NavigationReference entries that
+// might need resolved. It recursively walks the jsonResponse object, looking
+// for links at every level, and returns a list (out) of locations within the
+// tree that need to be expanded. The current json pointer location p is passed
+// in to reference the current node that's being expanded, so it can be combined
+// with the keys from the jsonResponse object
+inline void findNavigationReferencesRecursive(
+ ExpandType eType, nlohmann::json& jsonResponse,
+ const nlohmann::json::json_pointer& p, bool inLinks,
+ std::vector<ExpandNode>& out)
+{
+ // If no expand is needed, return early
+ if (eType == ExpandType::None)
+ {
+ return;
+ }
+ nlohmann::json::array_t* array =
+ jsonResponse.get_ptr<nlohmann::json::array_t*>();
+ if (array != nullptr)
+ {
+ size_t index = 0;
+ // For arrays, walk every element in the array
+ for (auto& element : *array)
+ {
+ nlohmann::json::json_pointer newPtr = p / index;
+ BMCWEB_LOG_DEBUG << "Traversing response at " << newPtr.to_string();
+ findNavigationReferencesRecursive(eType, element, newPtr, inLinks,
+ out);
+ index++;
+ }
+ }
+ nlohmann::json::object_t* obj =
+ jsonResponse.get_ptr<nlohmann::json::object_t*>();
+ if (obj == nullptr)
+ {
+ return;
+ }
+ // Navigation References only ever have a single element
+ if (obj->size() == 1)
+ {
+ if (obj->begin()->first == "@odata.id")
+ {
+ const std::string* uri =
+ obj->begin()->second.get_ptr<const std::string*>();
+ if (uri != nullptr)
+ {
+ BMCWEB_LOG_DEBUG << "Found element at " << p.to_string();
+ out.push_back({p, *uri});
+ }
+ }
+ }
+ // Loop the object and look for links
+ for (auto& element : *obj)
+ {
+ if (!inLinks)
+ {
+ // Check if this is a links node
+ inLinks = element.first == "Links";
+ }
+ // Only traverse the parts of the tree the user asked for
+ // Per section 7.3 of the redfish specification
+ if (inLinks && eType == ExpandType::NotLinks)
+ {
+ continue;
+ }
+ if (!inLinks && eType == ExpandType::Links)
+ {
+ continue;
+ }
+ nlohmann::json::json_pointer newPtr = p / element.first;
+ BMCWEB_LOG_DEBUG << "Traversing response at " << newPtr;
+
+ findNavigationReferencesRecursive(eType, element.second, newPtr,
+ inLinks, out);
+ }
+}
+
+inline std::vector<ExpandNode>
+ findNavigationReferences(ExpandType eType, nlohmann::json& jsonResponse,
+ const nlohmann::json::json_pointer& root)
+{
+ std::vector<ExpandNode> ret;
+ findNavigationReferencesRecursive(eType, jsonResponse, root, false, ret);
+ return ret;
+}
+
+class MultiAsyncResp : public std::enable_shared_from_this<MultiAsyncResp>
+{
+ public:
+ // This object takes a single asyncResp object as the "final" one, then
+ // allows callers to attach sub-responses within the json tree that need
+ // to be executed and filled into their appropriate locations. This
+ // class manages the final "merge" of the json resources.
+ MultiAsyncResp(crow::App& app,
+ std::shared_ptr<bmcweb::AsyncResp> finalResIn) :
+ app(app),
+ finalRes(std::move(finalResIn))
+ {}
+
+ void addAwaitingResponse(
+ Query query, std::shared_ptr<bmcweb::AsyncResp>& res,
+ const nlohmann::json::json_pointer& finalExpandLocation)
+ {
+ res->res.setCompleteRequestHandler(std::bind_front(
+ onEndStatic, shared_from_this(), query, finalExpandLocation));
+ }
+
+ void onEnd(Query query, const nlohmann::json::json_pointer& locationToPlace,
+ crow::Response& res)
+ {
+ nlohmann::json& finalObj = finalRes->res.jsonValue[locationToPlace];
+ finalObj = std::move(res.jsonValue);
+
+ if (query.expandLevel <= 0)
+ {
+ // Last level to expand, no need to go deeper
+ return;
+ }
+ // Now decrease the depth by one to account for the tree node we
+ // just resolved
+ query.expandLevel--;
+
+ std::vector<ExpandNode> nodes = findNavigationReferences(
+ query.expandType, finalObj, locationToPlace);
+ BMCWEB_LOG_DEBUG << nodes.size() << " nodes to traverse";
+ for (const ExpandNode& node : nodes)
+ {
+ BMCWEB_LOG_DEBUG << "Expanding " << locationToPlace;
+ std::error_code ec;
+ crow::Request newReq({boost::beast::http::verb::get, node.uri, 11},
+ ec);
+ if (ec)
+ {
+ messages::internalError(res);
+ return;
+ }
+
+ auto asyncResp = std::make_shared<bmcweb::AsyncResp>();
+ BMCWEB_LOG_DEBUG << "setting completion handler on "
+ << &asyncResp->res;
+ addAwaitingResponse(query, asyncResp, node.location);
+ app.handle(newReq, asyncResp);
+ }
+ }
+
+ private:
+ static void onEndStatic(const std::shared_ptr<MultiAsyncResp>& multi,
+ Query query,
+ const nlohmann::json::json_pointer& locationToPlace,
+ crow::Response& res)
+ {
+ multi->onEnd(query, locationToPlace, res);
+ }
+
+ crow::App& app;
+ std::shared_ptr<bmcweb::AsyncResp> finalRes;
+};
+
+inline void
+ processAllParams(crow::App& app, const Query query,
+
+ std::function<void(crow::Response&)>& completionHandler,
+ crow::Response& intermediateResponse)
{
if (!completionHandler)
{
@@ -120,6 +372,21 @@ void processAllParams(crow::App& app, Query query,
processOnly(app, intermediateResponse, completionHandler);
return;
}
+ if (query.expandType != ExpandType::None)
+ {
+ BMCWEB_LOG_DEBUG << "Executing expand query";
+ // TODO(ed) this is a copy of the response object. Admittedly,
+ // we're inherently doing something inefficient, but we shouldn't
+ // have to do a full copy
+ auto asyncResp = std::make_shared<bmcweb::AsyncResp>();
+ asyncResp->res.setCompleteRequestHandler(std::move(completionHandler));
+ asyncResp->res.jsonValue = std::move(intermediateResponse.jsonValue);
+ auto multi = std::make_shared<MultiAsyncResp>(app, asyncResp);
+
+ // Start the chain by "ending" the root response
+ multi->onEnd(query, nlohmann::json::json_pointer(""), asyncResp->res);
+ return;
+ }
completionHandler(intermediateResponse);
}
diff --git a/redfish-core/include/utils/query_param_test.cpp b/redfish-core/include/utils/query_param_test.cpp
new file mode 100644
index 0000000000..69ed673f6d
--- /dev/null
+++ b/redfish-core/include/utils/query_param_test.cpp
@@ -0,0 +1,190 @@
+#include "query_param.hpp"
+
+#include <nlohmann/json.hpp>
+
+#include <iostream>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+TEST(QueryParams, ParseParametersOnly)
+{
+ auto ret = boost::urls::parse_relative_ref("/redfish/v1?only");
+ ASSERT_TRUE(ret);
+
+ crow::Response res;
+
+ using redfish::query_param::parseParameters;
+ using redfish::query_param::Query;
+ std::optional<Query> query = parseParameters(ret->params(), res);
+ ASSERT_TRUE(query != std::nullopt);
+ EXPECT_TRUE(query->isOnly);
+}
+
+TEST(QueryParams, ParseParametersExpand)
+{
+ auto ret = boost::urls::parse_relative_ref("/redfish/v1?$expand=*");
+ ASSERT_TRUE(ret);
+
+ crow::Response res;
+
+ using redfish::query_param::parseParameters;
+ using redfish::query_param::Query;
+ std::optional<Query> query = parseParameters(ret->params(), res);
+ ASSERT_TRUE(query != std::nullopt);
+ EXPECT_TRUE(query->expandType == redfish::query_param::ExpandType::Both);
+}
+
+TEST(QueryParams, ParseParametersUnexpectedGetsIgnored)
+{
+ auto ret = boost::urls::parse_relative_ref("/redfish/v1?unexpected_param");
+ ASSERT_TRUE(ret);
+
+ crow::Response res;
+
+ using redfish::query_param::parseParameters;
+ using redfish::query_param::Query;
+ std::optional<Query> query = parseParameters(ret->params(), res);
+ ASSERT_TRUE(query != std::nullopt);
+}
+
+TEST(QueryParams, ParseParametersUnexpectedDollarGetsError)
+{
+ auto ret = boost::urls::parse_relative_ref("/redfish/v1?$unexpected_param");
+ ASSERT_TRUE(ret);
+
+ crow::Response res;
+
+ using redfish::query_param::parseParameters;
+ using redfish::query_param::Query;
+ std::optional<Query> query = parseParameters(ret->params(), res);
+ ASSERT_TRUE(query == std::nullopt);
+ EXPECT_EQ(res.result(), boost::beast::http::status::not_implemented);
+}
+
+TEST(QueryParams, GetExpandType)
+{
+ redfish::query_param::Query query{};
+
+ EXPECT_FALSE(getExpandType("", query));
+ EXPECT_FALSE(getExpandType(".(", query));
+ EXPECT_FALSE(getExpandType(".()", query));
+ EXPECT_FALSE(getExpandType(".($levels=1", query));
+
+ EXPECT_TRUE(getExpandType("*", query));
+ EXPECT_EQ(query.expandType, redfish::query_param::ExpandType::Both);
+ EXPECT_TRUE(getExpandType(".", query));
+ EXPECT_EQ(query.expandType, redfish::query_param::ExpandType::NotLinks);
+ EXPECT_TRUE(getExpandType("~", query));
+ EXPECT_EQ(query.expandType, redfish::query_param::ExpandType::Links);
+
+ // Per redfish specification, level defaults to 1
+ EXPECT_TRUE(getExpandType(".", query));
+ EXPECT_EQ(query.expandLevel, 1);
+
+ EXPECT_TRUE(getExpandType(".($levels=42)", query));
+ EXPECT_EQ(query.expandLevel, 42);
+
+ // Overflow
+ EXPECT_FALSE(getExpandType(".($levels=256)", query));
+
+ // Negative
+ EXPECT_FALSE(getExpandType(".($levels=-1)", query));
+
+ // No number
+ EXPECT_FALSE(getExpandType(".($levels=a)", query));
+}
+
+namespace redfish::query_param
+{
+// NOLINTNEXTLINE(readability-identifier-naming)
+void PrintTo(const ExpandNode& value, ::std::ostream* os)
+{
+ *os << "ExpandNode: " << value.location << " " << value.uri;
+}
+}; // namespace redfish::query_param
+
+TEST(QueryParams, FindNavigationReferencesNonLink)
+{
+ using nlohmann::json;
+ using redfish::query_param::ExpandType;
+ using redfish::query_param::findNavigationReferences;
+ using ::testing::UnorderedElementsAre;
+ json singleTreeNode = R"({"Foo" : {"@odata.id": "/foobar"}})"_json;
+
+ // Parsing as the root should net one entry
+ EXPECT_THAT(findNavigationReferences(ExpandType::Both, singleTreeNode,
+ json::json_pointer("")),
+ UnorderedElementsAre(redfish::query_param::ExpandNode{
+ json::json_pointer("/Foo"), "/foobar"}));
+ // Parsing at a depth should net one entry at depth
+ EXPECT_THAT(findNavigationReferences(ExpandType::Both, singleTreeNode,
+ json::json_pointer("/baz")),
+ UnorderedElementsAre(redfish::query_param::ExpandNode{
+ json::json_pointer("/baz/Foo"), "/foobar"}));
+
+ // Parsing in Non-hyperlinks mode should net one entry
+ EXPECT_THAT(findNavigationReferences(ExpandType::NotLinks, singleTreeNode,
+ json::json_pointer("")),
+ UnorderedElementsAre(redfish::query_param::ExpandNode{
+ json::json_pointer("/Foo"), "/foobar"}));
+
+ // Parsing non-hyperlinks at depth should net one entry at depth
+ EXPECT_THAT(findNavigationReferences(ExpandType::NotLinks, singleTreeNode,
+ json::json_pointer("/baz")),
+ UnorderedElementsAre(redfish::query_param::ExpandNode{
+ json::json_pointer("/baz/Foo"), "/foobar"}));
+
+ // Searching for not types should return empty set
+ EXPECT_TRUE(findNavigationReferences(ExpandType::None, singleTreeNode,
+ json::json_pointer(""))
+ .empty());
+
+ // Searching for hyperlinks only should return empty set
+ EXPECT_TRUE(findNavigationReferences(ExpandType::Links, singleTreeNode,
+ json::json_pointer(""))
+ .empty());
+}
+
+TEST(QueryParams, FindNavigationReferencesLink)
+{
+ using nlohmann::json;
+ using redfish::query_param::ExpandType;
+ using redfish::query_param::findNavigationReferences;
+ using ::testing::UnorderedElementsAre;
+ json singleLinkNode =
+ R"({"Links" : {"Sessions": {"@odata.id": "/foobar"}}})"_json;
+
+ // Parsing as the root should net one entry
+ EXPECT_THAT(findNavigationReferences(ExpandType::Both, singleLinkNode,
+ json::json_pointer("")),
+ UnorderedElementsAre(redfish::query_param::ExpandNode{
+ json::json_pointer("/Links/Sessions"), "/foobar"}));
+ // Parsing at a depth should net one entry at depth
+ EXPECT_THAT(findNavigationReferences(ExpandType::Both, singleLinkNode,
+ json::json_pointer("/baz")),
+ UnorderedElementsAre(redfish::query_param::ExpandNode{
+ json::json_pointer("/baz/Links/Sessions"), "/foobar"}));
+
+ // Parsing in hyperlinks mode should net one entry
+ EXPECT_THAT(findNavigationReferences(ExpandType::Links, singleLinkNode,
+ json::json_pointer("")),
+ UnorderedElementsAre(redfish::query_param::ExpandNode{
+ json::json_pointer("/Links/Sessions"), "/foobar"}));
+
+ // Parsing hyperlinks at depth should net one entry at depth
+ EXPECT_THAT(findNavigationReferences(ExpandType::Links, singleLinkNode,
+ json::json_pointer("/baz")),
+ UnorderedElementsAre(redfish::query_param::ExpandNode{
+ json::json_pointer("/baz/Links/Sessions"), "/foobar"}));
+
+ // Searching for not types should return empty set
+ EXPECT_TRUE(findNavigationReferences(ExpandType::None, singleLinkNode,
+ json::json_pointer(""))
+ .empty());
+
+ // Searching for non-hyperlinks only should return empty set
+ EXPECT_TRUE(findNavigationReferences(ExpandType::NotLinks, singleLinkNode,
+ json::json_pointer(""))
+ .empty());
+}
diff --git a/redfish-core/lib/service_root.hpp b/redfish-core/lib/service_root.hpp
index 696d6fd025..d7214eeb30 100644
--- a/redfish-core/lib/service_root.hpp
+++ b/redfish-core/lib/service_root.hpp
@@ -22,6 +22,7 @@
#include <http_request.hpp>
#include <nlohmann/json.hpp>
#include <persistent_data.hpp>
+#include <query.hpp>
#include <registries/privilege_registry.hpp>
#include <utils/systemd_utils.hpp>
@@ -29,10 +30,8 @@ namespace redfish
{
inline void
- handleServiceRootGet(const crow::Request& /*req*/,
- const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
+ handleServiceRootGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
{
-
std::string uuid = persistent_data::getConfig().systemUuid;
asyncResp->res.jsonValue["@odata.type"] =
"#ServiceRoot.v1_11_0.ServiceRoot";
@@ -73,10 +72,18 @@ inline void
nlohmann::json& protocolFeatures =
asyncResp->res.jsonValue["ProtocolFeaturesSupported"];
protocolFeatures["ExcerptQuery"] = false;
- protocolFeatures["ExpandQuery"]["ExpandAll"] = false;
- protocolFeatures["ExpandQuery"]["Levels"] = false;
- protocolFeatures["ExpandQuery"]["Links"] = false;
- protocolFeatures["ExpandQuery"]["NoLinks"] = false;
+
+ protocolFeatures["ExpandQuery"]["ExpandAll"] =
+ bmcwebInsecureEnableQueryParams;
+ // This is the maximum level defined in ServiceRoot.v1_13_0.json
+ if (bmcwebInsecureEnableQueryParams)
+ {
+ protocolFeatures["ExpandQuery"]["MaxLevels"] = 6;
+ }
+ protocolFeatures["ExpandQuery"]["Levels"] = bmcwebInsecureEnableQueryParams;
+ protocolFeatures["ExpandQuery"]["Links"] = bmcwebInsecureEnableQueryParams;
+ protocolFeatures["ExpandQuery"]["NoLinks"] =
+ bmcwebInsecureEnableQueryParams;
protocolFeatures["FilterQuery"] = false;
protocolFeatures["OnlyMemberQuery"] = bmcwebInsecureEnableQueryParams;
protocolFeatures["SelectQuery"] = false;
@@ -88,7 +95,15 @@ inline void requestRoutesServiceRoot(App& app)
{
BMCWEB_ROUTE(app, "/redfish/v1/")
.privileges(redfish::privileges::getServiceRoot)
- .methods(boost::beast::http::verb::get)(handleServiceRootGet);
+ .methods(boost::beast::http::verb::get)(
+ [&app](const crow::Request& req,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
+ if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
+ {
+ return;
+ }
+ handleServiceRootGet(asyncResp);
+ });
}
} // namespace redfish
diff --git a/redfish-core/lib/ut/service_root_test.cpp b/redfish-core/lib/ut/service_root_test.cpp
index 4c05751f09..1a7b249011 100644
--- a/redfish-core/lib/ut/service_root_test.cpp
+++ b/redfish-core/lib/ut/service_root_test.cpp
@@ -1,3 +1,5 @@
+#include "bmcweb_config.h"
+
#include "http_request.hpp"
#include "include/async_resp.hpp"
#include "nlohmann/json.hpp"
@@ -67,11 +69,24 @@ static void assertServiceRootGet(crow::Response& res)
EXPECT_EQ(json["ProtocolFeaturesSupported"].size(), 6);
EXPECT_FALSE(json["ProtocolFeaturesSupported"]["ExcerptQuery"]);
- EXPECT_FALSE(json["ProtocolFeaturesSupported"]["ExpandQuery"]["ExpandAll"]);
- EXPECT_FALSE(json["ProtocolFeaturesSupported"]["ExpandQuery"]["Levels"]);
- EXPECT_FALSE(json["ProtocolFeaturesSupported"]["ExpandQuery"]["Links"]);
- EXPECT_FALSE(json["ProtocolFeaturesSupported"]["ExpandQuery"]["NoLinks"]);
- EXPECT_EQ(json["ProtocolFeaturesSupported"]["ExpandQuery"].size(), 4);
+ EXPECT_EQ(json["ProtocolFeaturesSupported"]["ExpandQuery"]["ExpandAll"],
+ bmcwebInsecureEnableQueryParams);
+ EXPECT_EQ(json["ProtocolFeaturesSupported"]["ExpandQuery"]["Levels"],
+ bmcwebInsecureEnableQueryParams);
+ EXPECT_EQ(json["ProtocolFeaturesSupported"]["ExpandQuery"]["Links"],
+ bmcwebInsecureEnableQueryParams);
+ EXPECT_EQ(json["ProtocolFeaturesSupported"]["ExpandQuery"]["NoLinks"],
+ bmcwebInsecureEnableQueryParams);
+ if (bmcwebInsecureEnableQueryParams)
+ {
+ EXPECT_EQ(json["ProtocolFeaturesSupported"]["ExpandQuery"].size(), 5);
+ EXPECT_EQ(json["ProtocolFeaturesSupported"]["ExpandQuery"]["MaxLevels"],
+ 6);
+ }
+ else
+ {
+ EXPECT_EQ(json["ProtocolFeaturesSupported"]["ExpandQuery"].size(), 4);
+ }
EXPECT_FALSE(json["ProtocolFeaturesSupported"]["FilterQuery"]);
EXPECT_EQ(json["ProtocolFeaturesSupported"]["OnlyMemberQuery"],
bmcwebInsecureEnableQueryParams);
@@ -87,10 +102,9 @@ static void assertServiceRootGet(crow::Response& res)
TEST(ServiceRootTest, ServiceRootConstructor)
{
std::error_code ec;
- crow::Request req({}, ec);
auto shareAsyncResp = std::make_shared<bmcweb::AsyncResp>();
shareAsyncResp->res.setCompleteRequestHandler(assertServiceRootGet);
- redfish::handleServiceRootGet(req, shareAsyncResp);
+ redfish::handleServiceRootGet(shareAsyncResp);
}
diff --git a/redfish-core/src/query_param.cpp b/redfish-core/src/query_param.cpp
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/redfish-core/src/query_param.cpp