diff options
author | Ed Tanous <edtanous@google.com> | 2023-02-07 21:02:07 +0300 |
---|---|---|
committer | Ed Tanous <ed@tanous.net> | 2023-03-14 22:00:34 +0300 |
commit | ad595fa64f619124eadcdfc2af259a1975136399 (patch) | |
tree | da02f9b36676a5e389985ae0dc8c95e0642965d6 /test | |
parent | 5315c1b149b724d0395f42ca75d4660aaecdf351 (diff) | |
download | bmcweb-ad595fa64f619124eadcdfc2af259a1975136399.tar.xz |
Only parse to the depth requested
There are cases in aggregation where an expand parameter might get
forwarded to a client. Because our previous expand algorithm assumed
that any endpoint within bmcweb would only produce "depth=1" responses,
it was reasonable to assume that the pre-response could not contain
expanded content. Aggregated resources can't make that assumption.
This commit attempts to pass through depth through the request, to
ensure that we only expand the level that the user requested, and not
any level returned by the request. This is done by using the existence
of the resource identifer "@odata.id" to indicate each level in an
expanded response. This should be fine since the Redfish spec requires
that property to exist.
Added unit tests to cover aggregation scenarios. Modified existing
$expand tests to comply with the resource identifier dependency.
Tested:
New unit tests pass
Queried '/redfish/v1/Systems?$expand=.($levels=2)' on an aggregated
system whose satellite BMC supported $expand. The overall response was
correctly expanded for both resources on the aggregating BMC as well as
on the satellite BMC. Expanding the satellite resources did not require
sending multiple queries to the satellite.
Signed-off-by: Ed Tanous <edtanous@google.com>
Change-Id: I20ba60ee39bac11ffb3fe1768cec6299cf9ee13e
Signed-off-by: Carson Labrado <clabrado@google.com>
Diffstat (limited to 'test')
-rw-r--r-- | test/redfish-core/include/utils/query_param_test.cpp | 178 |
1 files changed, 162 insertions, 16 deletions
diff --git a/test/redfish-core/include/utils/query_param_test.cpp b/test/redfish-core/include/utils/query_param_test.cpp index 51f0b7101f..d049314d75 100644 --- a/test/redfish-core/include/utils/query_param_test.cpp +++ b/test/redfish-core/include/utils/query_param_test.cpp @@ -652,57 +652,203 @@ TEST(QueryParams, FindNavigationReferencesNonLink) { using nlohmann::json; - json singleTreeNode = R"({"Foo" : {"@odata.id": "/foobar"}})"_json; + // Responses must include their "@odata.id" property for $expand to work + // correctly + json singleTreeNode = + R"({"@odata.id": "/redfish/v1", + "Foo" : {"@odata.id": "/foobar"}})"_json; // Parsing as the root should net one entry - EXPECT_THAT(findNavigationReferences(ExpandType::Both, singleTreeNode), + EXPECT_THAT(findNavigationReferences(ExpandType::Both, 1, singleTreeNode), UnorderedElementsAre( ExpandNode{json::json_pointer("/Foo"), "/foobar"})); // Parsing in Non-hyperlinks mode should net one entry - EXPECT_THAT(findNavigationReferences(ExpandType::NotLinks, singleTreeNode), - UnorderedElementsAre( - ExpandNode{json::json_pointer("/Foo"), "/foobar"})); + EXPECT_THAT( + findNavigationReferences(ExpandType::NotLinks, 1, singleTreeNode), + UnorderedElementsAre( + ExpandNode{json::json_pointer("/Foo"), "/foobar"})); // Searching for not types should return empty set EXPECT_TRUE( - findNavigationReferences(ExpandType::None, singleTreeNode).empty()); + findNavigationReferences(ExpandType::None, 1, singleTreeNode).empty()); // Searching for hyperlinks only should return empty set EXPECT_TRUE( - findNavigationReferences(ExpandType::Links, singleTreeNode).empty()); + findNavigationReferences(ExpandType::Links, 1, singleTreeNode).empty()); + // Responses must include their "@odata.id" property for $expand to work + // correctly json multiTreeNodes = - R"({"Links": {"@odata.id": "/links"}, "Foo" : {"@odata.id": "/foobar"}})"_json; + R"({"@odata.id": "/redfish/v1", + "Links": {"@odata.id": "/links"}, + "Foo" : {"@odata.id": "/foobar"}})"_json; + // Should still find Foo - EXPECT_THAT(findNavigationReferences(ExpandType::NotLinks, multiTreeNodes), - UnorderedElementsAre( - ExpandNode{json::json_pointer("/Foo"), "/foobar"})); + EXPECT_THAT( + findNavigationReferences(ExpandType::NotLinks, 1, multiTreeNodes), + UnorderedElementsAre( + ExpandNode{json::json_pointer("/Foo"), "/foobar"})); } TEST(QueryParams, FindNavigationReferencesLink) { using nlohmann::json; + // Responses must include their "@odata.id" property for $expand to work + // correctly json singleLinkNode = - R"({"Links" : {"Sessions": {"@odata.id": "/foobar"}}})"_json; + R"({"@odata.id": "/redfish/v1", + "Links" : {"Sessions": {"@odata.id": "/foobar"}}})"_json; // Parsing as the root should net one entry - EXPECT_THAT(findNavigationReferences(ExpandType::Both, singleLinkNode), + EXPECT_THAT(findNavigationReferences(ExpandType::Both, 1, singleLinkNode), UnorderedElementsAre(ExpandNode{ json::json_pointer("/Links/Sessions"), "/foobar"})); // Parsing in hyperlinks mode should net one entry - EXPECT_THAT(findNavigationReferences(ExpandType::Links, singleLinkNode), + EXPECT_THAT(findNavigationReferences(ExpandType::Links, 1, singleLinkNode), UnorderedElementsAre(ExpandNode{ json::json_pointer("/Links/Sessions"), "/foobar"})); // Searching for not types should return empty set EXPECT_TRUE( - findNavigationReferences(ExpandType::None, singleLinkNode).empty()); + findNavigationReferences(ExpandType::None, 1, singleLinkNode).empty()); // Searching for non-hyperlinks only should return empty set EXPECT_TRUE( - findNavigationReferences(ExpandType::NotLinks, singleLinkNode).empty()); + findNavigationReferences(ExpandType::NotLinks, 1, singleLinkNode) + .empty()); +} + +TEST(QueryParams, PreviouslyExpanded) +{ + using nlohmann::json; + + // Responses must include their "@odata.id" property for $expand to work + // correctly + json expNode = json::parse(R"( +{ + "@odata.id": "/redfish/v1/Chassis", + "@odata.type": "#ChassisCollection.ChassisCollection", + "Members": [ + { + "@odata.id": "/redfish/v1/Chassis/5B247A_Sat1", + "@odata.type": "#Chassis.v1_17_0.Chassis", + "Sensors": { + "@odata.id": "/redfish/v1/Chassis/5B247A_Sat1/Sensors" + } + }, + { + "@odata.id": "/redfish/v1/Chassis/5B247A_Sat2", + "@odata.type": "#Chassis.v1_17_0.Chassis", + "Sensors": { + "@odata.id": "/redfish/v1/Chassis/5B247A_Sat2/Sensors" + } + } + ], + "Members@odata.count": 2, + "Name": "Chassis Collection" +} +)", + nullptr, false); + + // Expand has already occurred so we should not do anything + EXPECT_TRUE( + findNavigationReferences(ExpandType::NotLinks, 1, expNode).empty()); + + // Previous expand was only a single level so we should further expand + EXPECT_THAT(findNavigationReferences(ExpandType::NotLinks, 2, expNode), + UnorderedElementsAre( + ExpandNode{json::json_pointer("/Members/0/Sensors"), + "/redfish/v1/Chassis/5B247A_Sat1/Sensors"}, + ExpandNode{json::json_pointer("/Members/1/Sensors"), + "/redfish/v1/Chassis/5B247A_Sat2/Sensors"})); + + // Make sure we can handle when an array was expanded further down the tree + json expNode2 = R"({"@odata.id" : "/redfish/v1"})"_json; + expNode2["Chassis"] = std::move(expNode); + EXPECT_TRUE( + findNavigationReferences(ExpandType::NotLinks, 1, expNode2).empty()); + EXPECT_TRUE( + findNavigationReferences(ExpandType::NotLinks, 2, expNode2).empty()); + + // Previous expand was two levels so we should further expand + EXPECT_THAT(findNavigationReferences(ExpandType::NotLinks, 3, expNode2), + UnorderedElementsAre( + ExpandNode{json::json_pointer("/Chassis/Members/0/Sensors"), + "/redfish/v1/Chassis/5B247A_Sat1/Sensors"}, + ExpandNode{json::json_pointer("/Chassis/Members/1/Sensors"), + "/redfish/v1/Chassis/5B247A_Sat2/Sensors"})); +} + +TEST(QueryParams, PartiallyPreviouslyExpanded) +{ + using nlohmann::json; + + // Responses must include their "@odata.id" property for $expand to work + // correctly + json expNode = json::parse(R"( +{ + "@odata.id": "/redfish/v1/Chassis", + "@odata.type": "#ChassisCollection.ChassisCollection", + "Members": [ + { + "@odata.id": "/redfish/v1/Chassis/Local" + }, + { + "@odata.id": "/redfish/v1/Chassis/5B247A_Sat1", + "@odata.type": "#Chassis.v1_17_0.Chassis", + "Sensors": { + "@odata.id": "/redfish/v1/Chassis/5B247A_Sat1/Sensors" + } + } + ], + "Members@odata.count": 2, + "Name": "Chassis Collection" +} +)", + nullptr, false); + + // 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), + 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), + UnorderedElementsAre( + ExpandNode{json::json_pointer("/Members/0"), + "/redfish/v1/Chassis/Local"}, + ExpandNode{json::json_pointer("/Members/1/Sensors"), + "/redfish/v1/Chassis/5B247A_Sat1/Sensors"})); + + // Now the response has paths that have been expanded 0, 1, and 2 times + json expNode2 = R"({"@odata.id" : "/redfish/v1", + "Systems": {"@odata.id": "/redfish/v1/Systems"}})"_json; + expNode2["Chassis"] = std::move(expNode); + + EXPECT_THAT(findNavigationReferences(ExpandType::NotLinks, 1, expNode2), + UnorderedElementsAre(ExpandNode{json::json_pointer("/Systems"), + "/redfish/v1/Systems"})); + + EXPECT_THAT( + findNavigationReferences(ExpandType::NotLinks, 2, 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), + UnorderedElementsAre( + ExpandNode{json::json_pointer("/Systems"), "/redfish/v1/Systems"}, + ExpandNode{json::json_pointer("/Chassis/Members/0"), + "/redfish/v1/Chassis/Local"}, + ExpandNode{json::json_pointer("/Chassis/Members/1/Sensors"), + "/redfish/v1/Chassis/5B247A_Sat1/Sensors"})); } } // namespace |