diff options
author | Ed Tanous <edtanous@google.com> | 2022-04-05 20:26:56 +0300 |
---|---|---|
committer | Ed Tanous <ed@tanous.net> | 2022-05-10 19:08:25 +0300 |
commit | 2a68dc80d62482bfa886e78e536e223b84094ad3 (patch) | |
tree | 2b0618b7cfa94ead72eb0d3d02338cdfd7cc03d3 | |
parent | 550a6bf85f81c1725b6c320a5ee419335cff2cf6 (diff) | |
download | bmcweb-2a68dc80d62482bfa886e78e536e223b84094ad3.tar.xz |
Implement $top and $skip
$top and $skip are parameters for controlling the responses of
collections, to limit their size, per the Redfish specification section
7.4.
$skip=integer "Applies to resource collections. Returns a subset of the
members in a resource collection, or an empty set of members if the
$skip value is greater than or equal to the member count. This paging
query parameter defines the number of members in the resource collection
to skip."
$top=<integer> "Applies to resource collections. Defines the number of
members to show in the response. Minimum value is 0 , though a value of
0 returns an empty set of members."
This commit implements them within the resource query.
Tested:
curl --insecure --user root:0penBmc https://localhost:18080/redfish/v1/Registries\?\$top\=1
Returns 1 value. Walking through values of 1-5 (there are 4 registries
currently) returns the appropriate sizes of collection (with 5 returning
4 entries).
curl --insecure --user root:0penBmc https://localhost:18080/redfish/v1/Registries\?\$skip\=0
Returns the collection. $skip values of 0-5 return descending number of
results.
Signed-off-by: Ed Tanous <edtanous@google.com>
Change-Id: Ied8a8f8338f119173509fb4b7ba2bd4a6c49cae8
-rw-r--r-- | redfish-core/include/utils/query_param.hpp | 48 | ||||
-rw-r--r-- | redfish-core/include/utils/query_param_test.cpp | 67 |
2 files changed, 114 insertions, 1 deletions
diff --git a/redfish-core/include/utils/query_param.hpp b/redfish-core/include/utils/query_param.hpp index b4a18a3ec2..5c43255de9 100644 --- a/redfish-core/include/utils/query_param.hpp +++ b/redfish-core/include/utils/query_param.hpp @@ -494,9 +494,49 @@ class MultiAsyncResp : public std::enable_shared_from_this<MultiAsyncResp> std::shared_ptr<bmcweb::AsyncResp> finalRes; }; +inline void processTopAndSkip(const Query& query, crow::Response& res) +{ + nlohmann::json::object_t* obj = + res.jsonValue.get_ptr<nlohmann::json::object_t*>(); + if (obj == nullptr) + { + // Shouldn't be possible. All responses should be objects. + messages::internalError(res); + return; + } + + BMCWEB_LOG_DEBUG << "Handling top/skip"; + nlohmann::json::object_t::iterator members = obj->find("Members"); + if (members == obj->end()) + { + // From the Redfish specification 7.3.1 + // ... the HTTP 400 Bad Request status code with the + // QueryNotSupportedOnResource message from the Base Message Registry + // for any supported query parameters that apply only to resource + // collections but are used on singular resources. + messages::queryNotSupportedOnResource(res); + return; + } + + nlohmann::json::array_t* arr = + members->second.get_ptr<nlohmann::json::array_t*>(); + if (arr == nullptr) + { + messages::internalError(res); + return; + } + + // Per section 7.3.1 of the Redfish specification, $skip is run before $top + // Can only skip as many values as we have + size_t skip = std::min(arr->size(), query.skip); + arr->erase(arr->begin(), arr->begin() + static_cast<ssize_t>(skip)); + + size_t top = std::min(arr->size(), query.top); + arr->erase(arr->begin() + static_cast<ssize_t>(top), arr->end()); +} + inline void processAllParams(crow::App& app, const Query query, - std::function<void(crow::Response&)>& completionHandler, crow::Response& intermediateResponse) { @@ -520,6 +560,12 @@ inline void processOnly(app, intermediateResponse, completionHandler); return; } + + if (query.top != std::numeric_limits<size_t>::max() || query.skip != 0) + { + processTopAndSkip(query, intermediateResponse); + } + if (query.expandType != ExpandType::None) { BMCWEB_LOG_DEBUG << "Executing expand query"; diff --git a/redfish-core/include/utils/query_param_test.cpp b/redfish-core/include/utils/query_param_test.cpp index e5d8de753a..93013c2f3f 100644 --- a/redfish-core/include/utils/query_param_test.cpp +++ b/redfish-core/include/utils/query_param_test.cpp @@ -150,6 +150,73 @@ TEST(QueryParams, ParseParametersExpand) } } +TEST(QueryParams, ParseParametersTop) +{ + auto ret = boost::urls::parse_relative_ref("/redfish/v1?$top=1"); + 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(query->top, 1); +} + +TEST(QueryParams, ParseParametersTopOutOfRangeNegative) +{ + auto ret = boost::urls::parse_relative_ref("/redfish/v1?$top=-1"); + 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, ParseParametersTopOutOfRangePositive) +{ + auto ret = boost::urls::parse_relative_ref("/redfish/v1?$top=1001"); + 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, ParseParametersSkip) +{ + auto ret = boost::urls::parse_relative_ref("/redfish/v1?$skip=1"); + 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(query->skip, 1); +} +TEST(QueryParams, ParseParametersSkipOutOfRange) +{ + auto ret = boost::urls::parse_relative_ref( + "/redfish/v1?$skip=99999999999999999999"); + 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_EQ(query, std::nullopt); +} + TEST(QueryParams, ParseParametersUnexpectedGetsIgnored) { auto ret = boost::urls::parse_relative_ref("/redfish/v1?unexpected_param"); |