summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWilly Tu <wltu@google.com>2023-05-23 20:58:39 +0300
committerEd Tanous <ed@tanous.net>2023-06-12 18:33:22 +0300
commit32cdb4a78399fec17442dc2cd36b2e57382475a3 (patch)
tree8f0c963ca453ee1a5e3808a23d338d527d583c96
parent994fd86a3f6649a820f66313765e85e762ad105a (diff)
downloadbmcweb-32cdb4a78399fec17442dc2cd36b2e57382475a3.tar.xz
query: Fix default expand level with delegated
With delegate expand, the default expand level is -= `queryCapabilities.canDelegateExpandLevel`. This creates an overlap of expand process between delegate expand vs. default expand. With query.expandLevel = 2 -> query.expandLevel = 1 and delegated.expandLevel = 1. Both delegated and default expand will try to only expand of level one instead of level 2 for the default. The code in https://github.com/openbmc/bmcweb/blob/479e899d5f57a67647f83b7f615d2c8565290bcf/redfish-core/include/utils/query_param.hpp#L583-L597 stated that the level with "@odata.id" + other property is treated as a seperate level. So with `query.expandLevel = 1` it just loop through the id that was already expanded and is noop. Tested: Before: /redfish/v1/Chassis/BMC/Sensors?$expand=.($levels=2) returns the same result as level=1. Needs level=3 to expand to the next level. The RelatedItem in here doesn't get expanded with level=2. ``` wget -qO- 'http://localhost:80/redfish/v1/Chassis/BMC/Sensors?$expand=.($levels=1)' ... { "@odata.id": "/redfish/v1/Chassis/BMC/Sensors/temperature_DIMMXX", "@odata.type": "#Sensor.v1_2_0.Sensor", "Id": "temperature_DIMMXX", "Name": "DIMMXX", "Reading": 30.0, "ReadingRangeMax": 127.0, "ReadingRangeMin": -128.0, "ReadingType": "Temperature", "ReadingUnits": "Cel", "RelatedItem": [ { "@odata.id": "/redfish/v1/Systems/system/Memory/dimmXX" } ], "Status": { "Health": "OK", "State": "Enabled" }, "Thresholds": { "LowerCaution": { "Reading": null }, "LowerCritical": { "Reading": null }, "UpperCaution": { "Reading": 93.0 }, "UpperCritical": { "Reading": 95.0 } } } ], "Members@odata.count": 242, "Name": "Sensors" } ``` After: level=2 was able to expand to the next level. Change-Id: I542177a94a33f8df7afbb68837f3a53b86140c86 Signed-off-by: Willy Tu <wltu@google.com>
-rw-r--r--redfish-core/include/query.hpp6
-rw-r--r--redfish-core/include/utils/query_param.hpp48
-rw-r--r--test/redfish-core/include/utils/query_param_test.cpp93
3 files changed, 100 insertions, 47 deletions
diff --git a/redfish-core/include/query.hpp b/redfish-core/include/query.hpp
index 10a337b96b..c1ca38e957 100644
--- a/redfish-core/include/query.hpp
+++ b/redfish-core/include/query.hpp
@@ -166,9 +166,9 @@ inline bool handleIfMatch(crow::App& app, const crow::Request& req,
asyncResp->res.releaseCompleteRequestHandler();
asyncResp->res.setCompleteRequestHandler(
- [&app, handler(std::move(handler)),
- query{std::move(*queryOpt)}](crow::Response& resIn) mutable {
- processAllParams(app, query, handler, resIn);
+ [&app, handler(std::move(handler)), query{std::move(*queryOpt)},
+ delegated{delegated}](crow::Response& resIn) mutable {
+ processAllParams(app, query, delegated, handler, resIn);
});
return needToCallHandlers;
diff --git a/redfish-core/include/utils/query_param.hpp b/redfish-core/include/utils/query_param.hpp
index 71fcbd7e05..e5402d50e3 100644
--- a/redfish-core/include/utils/query_param.hpp
+++ b/redfish-core/include/utils/query_param.hpp
@@ -221,7 +221,6 @@ inline Query delegate(const QueryCapabilities& queryCapabilities, Query& query)
}
else
{
- query.expandLevel -= queryCapabilities.canDelegateExpandLevel;
delegated.expandLevel = queryCapabilities.canDelegateExpandLevel;
}
}
@@ -534,8 +533,8 @@ struct ExpandNode
// with the keys from the jsonResponse object
inline void findNavigationReferencesRecursive(
ExpandType eType, nlohmann::json& jsonResponse,
- const nlohmann::json::json_pointer& p, int depth, bool inLinks,
- std::vector<ExpandNode>& out)
+ const nlohmann::json::json_pointer& p, int depth, int skipDepth,
+ bool inLinks, std::vector<ExpandNode>& out)
{
// If no expand is needed, return early
if (eType == ExpandType::None)
@@ -554,7 +553,7 @@ inline void findNavigationReferencesRecursive(
nlohmann::json::json_pointer newPtr = p / index;
BMCWEB_LOG_DEBUG << "Traversing response at " << newPtr.to_string();
findNavigationReferencesRecursive(eType, element, newPtr, depth,
- inLinks, out);
+ skipDepth, inLinks, out);
index++;
}
}
@@ -574,7 +573,10 @@ inline void findNavigationReferencesRecursive(
if (uri != nullptr)
{
BMCWEB_LOG_DEBUG << "Found " << *uri << " at " << p.to_string();
- out.push_back({p, *uri});
+ if (skipDepth == 0)
+ {
+ out.push_back({p, *uri});
+ }
return;
}
}
@@ -589,11 +591,22 @@ inline void findNavigationReferencesRecursive(
// "@odata.id" then that means we have entered a new level / expanded
// resource. We need to stop traversing if we're already at the desired
// depth
- if ((obj->size() > 1) && (depth == 0))
+ if (obj->size() > 1)
{
- return;
+ if (depth == 0)
+ {
+ return;
+ }
+ if (skipDepth > 0)
+ {
+ skipDepth--;
+ }
+ }
+
+ if (skipDepth == 0)
+ {
+ newDepth--;
}
- newDepth--;
}
// Loop the object and look for links
@@ -619,7 +632,8 @@ inline void findNavigationReferencesRecursive(
BMCWEB_LOG_DEBUG << "Traversing response at " << newPtr;
findNavigationReferencesRecursive(eType, element.second, newPtr,
- newDepth, localInLinks, out);
+ newDepth, skipDepth, localInLinks,
+ out);
}
}
@@ -630,13 +644,14 @@ inline void findNavigationReferencesRecursive(
// lands. May want to avoid forwarding query params when request is uptree from
// a top level collection.
inline std::vector<ExpandNode>
- findNavigationReferences(ExpandType eType, int depth,
+ findNavigationReferences(ExpandType eType, int depth, int skipDepth,
nlohmann::json& jsonResponse)
{
std::vector<ExpandNode> ret;
const nlohmann::json::json_pointer root = nlohmann::json::json_pointer("");
- findNavigationReferencesRecursive(eType, jsonResponse, root, depth, false,
- ret);
+ // SkipDepth +1 since we are skipping the root by default.
+ findNavigationReferencesRecursive(eType, jsonResponse, root, depth,
+ skipDepth + 1, false, ret);
return ret;
}
@@ -795,10 +810,11 @@ class MultiAsyncResp : public std::enable_shared_from_this<MultiAsyncResp>
// Handles the very first level of Expand, and starts a chain of sub-queries
// for deeper levels.
- void startQuery(const Query& query)
+ void startQuery(const Query& query, const Query& delegated)
{
std::vector<ExpandNode> nodes = findNavigationReferences(
- query.expandType, query.expandLevel, finalRes->res.jsonValue);
+ query.expandType, query.expandLevel, delegated.expandLevel,
+ finalRes->res.jsonValue);
BMCWEB_LOG_DEBUG << nodes.size() << " nodes to traverse";
const std::optional<std::string> queryStr = formatQueryForExpand(query);
if (!queryStr)
@@ -960,7 +976,7 @@ inline void processSelect(crow::Response& intermediateResponse,
}
inline void
- processAllParams(crow::App& app, const Query& query,
+ processAllParams(crow::App& app, const Query& query, const Query& delegated,
std::function<void(crow::Response&)>& completionHandler,
crow::Response& intermediateResponse)
{
@@ -998,7 +1014,7 @@ inline void
asyncResp->res.setCompleteRequestHandler(std::move(completionHandler));
auto multi = std::make_shared<MultiAsyncResp>(app, asyncResp);
- multi->startQuery(query);
+ multi->startQuery(query, delegated);
return;
}
diff --git a/test/redfish-core/include/utils/query_param_test.cpp b/test/redfish-core/include/utils/query_param_test.cpp
index 8c75241d77..7aa4ad519b 100644
--- a/test/redfish-core/include/utils/query_param_test.cpp
+++ b/test/redfish-core/include/utils/query_param_test.cpp
@@ -53,7 +53,7 @@ TEST(Delegate, ExpandPositive)
EXPECT_FALSE(delegated.isOnly);
EXPECT_EQ(delegated.expandLevel, capabilities.canDelegateExpandLevel);
EXPECT_EQ(delegated.expandType, ExpandType::Both);
- EXPECT_EQ(query.expandLevel, 2);
+ EXPECT_EQ(query.expandLevel, 5);
}
TEST(Delegate, OnlyNegative)
@@ -659,23 +659,25 @@ TEST(QueryParams, FindNavigationReferencesNonLink)
"Foo" : {"@odata.id": "/foobar"}})"_json;
// Parsing as the root should net one entry
- EXPECT_THAT(findNavigationReferences(ExpandType::Both, 1, singleTreeNode),
- UnorderedElementsAre(
- ExpandNode{json::json_pointer("/Foo"), "/foobar"}));
+ EXPECT_THAT(
+ findNavigationReferences(ExpandType::Both, 1, 0, singleTreeNode),
+ UnorderedElementsAre(
+ ExpandNode{json::json_pointer("/Foo"), "/foobar"}));
// Parsing in Non-hyperlinks mode should net one entry
EXPECT_THAT(
- findNavigationReferences(ExpandType::NotLinks, 1, singleTreeNode),
+ findNavigationReferences(ExpandType::NotLinks, 1, 0, singleTreeNode),
UnorderedElementsAre(
ExpandNode{json::json_pointer("/Foo"), "/foobar"}));
// Searching for not types should return empty set
- EXPECT_TRUE(
- findNavigationReferences(ExpandType::None, 1, singleTreeNode).empty());
+ EXPECT_TRUE(findNavigationReferences(ExpandType::None, 1, 0, singleTreeNode)
+ .empty());
// Searching for hyperlinks only should return empty set
EXPECT_TRUE(
- findNavigationReferences(ExpandType::Links, 1, singleTreeNode).empty());
+ findNavigationReferences(ExpandType::Links, 1, 0, singleTreeNode)
+ .empty());
// Responses must include their "@odata.id" property for $expand to work
// correctly
@@ -686,7 +688,7 @@ TEST(QueryParams, FindNavigationReferencesNonLink)
// Should still find Foo
EXPECT_THAT(
- findNavigationReferences(ExpandType::NotLinks, 1, multiTreeNodes),
+ findNavigationReferences(ExpandType::NotLinks, 1, 0, multiTreeNodes),
UnorderedElementsAre(
ExpandNode{json::json_pointer("/Foo"), "/foobar"}));
}
@@ -702,21 +704,23 @@ TEST(QueryParams, FindNavigationReferencesLink)
"Links" : {"Sessions": {"@odata.id": "/foobar"}}})"_json;
// Parsing as the root should net one entry
- EXPECT_THAT(findNavigationReferences(ExpandType::Both, 1, singleLinkNode),
- UnorderedElementsAre(ExpandNode{
- json::json_pointer("/Links/Sessions"), "/foobar"}));
+ EXPECT_THAT(
+ findNavigationReferences(ExpandType::Both, 1, 0, singleLinkNode),
+ UnorderedElementsAre(
+ ExpandNode{json::json_pointer("/Links/Sessions"), "/foobar"}));
// Parsing in hyperlinks mode should net one entry
- EXPECT_THAT(findNavigationReferences(ExpandType::Links, 1, singleLinkNode),
- UnorderedElementsAre(ExpandNode{
- json::json_pointer("/Links/Sessions"), "/foobar"}));
+ EXPECT_THAT(
+ findNavigationReferences(ExpandType::Links, 1, 0, singleLinkNode),
+ UnorderedElementsAre(
+ ExpandNode{json::json_pointer("/Links/Sessions"), "/foobar"}));
// Searching for not types should return empty set
- EXPECT_TRUE(
- findNavigationReferences(ExpandType::None, 1, singleLinkNode).empty());
+ EXPECT_TRUE(findNavigationReferences(ExpandType::None, 1, 0, singleLinkNode)
+ .empty());
// Searching for non-hyperlinks only should return empty set
EXPECT_TRUE(
- findNavigationReferences(ExpandType::NotLinks, 1, singleLinkNode)
+ findNavigationReferences(ExpandType::NotLinks, 1, 0, singleLinkNode)
.empty());
}
@@ -754,10 +758,10 @@ TEST(QueryParams, PreviouslyExpanded)
// Expand has already occurred so we should not do anything
EXPECT_TRUE(
- findNavigationReferences(ExpandType::NotLinks, 1, expNode).empty());
+ findNavigationReferences(ExpandType::NotLinks, 1, 0, expNode).empty());
// Previous expand was only a single level so we should further expand
- EXPECT_THAT(findNavigationReferences(ExpandType::NotLinks, 2, expNode),
+ EXPECT_THAT(findNavigationReferences(ExpandType::NotLinks, 2, 0, expNode),
UnorderedElementsAre(
ExpandNode{json::json_pointer("/Members/0/Sensors"),
"/redfish/v1/Chassis/5B247A_Sat1/Sensors"},
@@ -768,12 +772,12 @@ TEST(QueryParams, PreviouslyExpanded)
json expNode2 = R"({"@odata.id" : "/redfish/v1"})"_json;
expNode2["Chassis"] = std::move(expNode);
EXPECT_TRUE(
- findNavigationReferences(ExpandType::NotLinks, 1, expNode2).empty());
+ findNavigationReferences(ExpandType::NotLinks, 1, 0, expNode2).empty());
EXPECT_TRUE(
- findNavigationReferences(ExpandType::NotLinks, 2, expNode2).empty());
+ findNavigationReferences(ExpandType::NotLinks, 2, 0, expNode2).empty());
// Previous expand was two levels so we should further expand
- EXPECT_THAT(findNavigationReferences(ExpandType::NotLinks, 3, expNode2),
+ EXPECT_THAT(findNavigationReferences(ExpandType::NotLinks, 3, 0, expNode2),
UnorderedElementsAre(
ExpandNode{json::json_pointer("/Chassis/Members/0/Sensors"),
"/redfish/v1/Chassis/5B247A_Sat1/Sensors"},
@@ -781,6 +785,39 @@ TEST(QueryParams, PreviouslyExpanded)
"/redfish/v1/Chassis/5B247A_Sat2/Sensors"}));
}
+TEST(QueryParams, DelegatedSkipExpanded)
+{
+ using nlohmann::json;
+
+ // Responses must include their "@odata.id" property for $expand to work
+ // correctly
+ json expNode = json::parse(R"(
+{
+ "@odata.id": "/redfish/v1",
+ "Foo": {
+ "@odata.id": "/foo"
+ },
+ "Bar": {
+ "@odata.id": "/bar",
+ "Foo": {
+ "@odata.id": "/barfoo"
+ }
+ }
+}
+)",
+ nullptr, false);
+
+ EXPECT_THAT(findNavigationReferences(ExpandType::NotLinks, 2, 0, expNode),
+ UnorderedElementsAre(
+ ExpandNode{json::json_pointer("/Foo"), "/foo"},
+ ExpandNode{json::json_pointer("/Bar/Foo"), "/barfoo"}));
+
+ // Skip the first expand level
+ EXPECT_THAT(findNavigationReferences(ExpandType::NotLinks, 1, 1, expNode),
+ UnorderedElementsAre(
+ ExpandNode{json::json_pointer("/Bar/Foo"), "/barfoo"}));
+}
+
TEST(QueryParams, PartiallyPreviouslyExpanded)
{
using nlohmann::json;
@@ -812,13 +849,13 @@ TEST(QueryParams, PartiallyPreviouslyExpanded)
// The 5B247A_Sat1 Chassis was already expanded a single level so we should
// only want to expand the Local Chassis
EXPECT_THAT(
- findNavigationReferences(ExpandType::NotLinks, 1, expNode),
+ findNavigationReferences(ExpandType::NotLinks, 1, 0, expNode),
UnorderedElementsAre(ExpandNode{json::json_pointer("/Members/0"),
"/redfish/v1/Chassis/Local"}));
// The 5B247A_Sat1 Chassis was already expanded a single level so we should
// further expand it as well as the Local Chassis
- EXPECT_THAT(findNavigationReferences(ExpandType::NotLinks, 2, expNode),
+ EXPECT_THAT(findNavigationReferences(ExpandType::NotLinks, 2, 0, expNode),
UnorderedElementsAre(
ExpandNode{json::json_pointer("/Members/0"),
"/redfish/v1/Chassis/Local"},
@@ -830,19 +867,19 @@ TEST(QueryParams, PartiallyPreviouslyExpanded)
"Systems": {"@odata.id": "/redfish/v1/Systems"}})"_json;
expNode2["Chassis"] = std::move(expNode);
- EXPECT_THAT(findNavigationReferences(ExpandType::NotLinks, 1, expNode2),
+ EXPECT_THAT(findNavigationReferences(ExpandType::NotLinks, 1, 0, expNode2),
UnorderedElementsAre(ExpandNode{json::json_pointer("/Systems"),
"/redfish/v1/Systems"}));
EXPECT_THAT(
- findNavigationReferences(ExpandType::NotLinks, 2, expNode2),
+ findNavigationReferences(ExpandType::NotLinks, 2, 0, expNode2),
UnorderedElementsAre(
ExpandNode{json::json_pointer("/Systems"), "/redfish/v1/Systems"},
ExpandNode{json::json_pointer("/Chassis/Members/0"),
"/redfish/v1/Chassis/Local"}));
EXPECT_THAT(
- findNavigationReferences(ExpandType::NotLinks, 3, expNode2),
+ findNavigationReferences(ExpandType::NotLinks, 3, 0, expNode2),
UnorderedElementsAre(
ExpandNode{json::json_pointer("/Systems"), "/redfish/v1/Systems"},
ExpandNode{json::json_pointer("/Chassis/Members/0"),