json utility: Update sort algorithms
Modified sort utility to be able to sort on a specified key.
New utility function sortJsonArrayByKey() added.
Note:
- Function odataObjectCmp() renamed to objectKeyCmp()
- New function odataObjectCmp() created which calls objectKeyCmp() with
@odata.id key specified.
- Comments for odataObjectCmp() didn't match behavior for object
without key. These objects are sorted as less than objects with the
key.
- sortJSONResponse() modified to use the new sortJsonArrayByKey().
Tested:
- Added new unit tests. These tests are in addition to the existing
tests. So they focus on testing comparing by different keys.
The existing tests already cover the different permutations of the
basic comparisons.
- Redfish Service validator passes
Change-Id: I949b7cb868c59a8eeda3798e6a82a1572bbc5792
Signed-off-by: Ed Tanous <etanous@nvidia.com>
Signed-off-by: Janet Adkins <janeta@us.ibm.com>
diff --git a/redfish-core/include/utils/json_utils.hpp b/redfish-core/include/utils/json_utils.hpp
index 2c0f5ef..4339e90 100644
--- a/redfish-core/include/utils/json_utils.hpp
+++ b/redfish-core/include/utils/json_utils.hpp
@@ -738,7 +738,8 @@
// Determines if two json objects are less, based on the presence of the
// @odata.id key
-inline int odataObjectCmp(const nlohmann::json& a, const nlohmann::json& b)
+inline int objectKeyCmp(std::string_view key, const nlohmann::json& a,
+ const nlohmann::json& b)
{
using object_t = nlohmann::json::object_t;
const object_t* aObj = a.get_ptr<const object_t*>();
@@ -756,9 +757,10 @@
{
return 1;
}
- object_t::const_iterator aIt = aObj->find("@odata.id");
- object_t::const_iterator bIt = bObj->find("@odata.id");
- // If either object doesn't have the key, they get "sorted" to the end.
+ object_t::const_iterator aIt = aObj->find(key);
+ object_t::const_iterator bIt = bObj->find(key);
+ // If either object doesn't have the key, they get "sorted" to the
+ // beginning.
if (aIt == aObj->end())
{
if (bIt == bObj->end())
@@ -776,7 +778,7 @@
const nlohmann::json::string_t* nameB =
bIt->second.get_ptr<const std::string*>();
// If either object doesn't have a string as the key, they get "sorted" to
- // the end.
+ // the beginning.
if (nameA == nullptr)
{
if (nameB == nullptr)
@@ -819,21 +821,41 @@
}
};
+// kept for backward compatibility
+inline int odataObjectCmp(const nlohmann::json& left,
+ const nlohmann::json& right)
+{
+ return objectKeyCmp("@odata.id", left, right);
+}
+
struct ODataObjectLess
{
+ std::string_view key;
+
+ explicit ODataObjectLess(std::string_view keyIn) : key(keyIn) {}
+
bool operator()(const nlohmann::json& left,
const nlohmann::json& right) const
{
- return odataObjectCmp(left, right) < 0;
+ return objectKeyCmp(key, left, right) < 0;
}
};
// Sort the JSON array by |element[key]|.
// Elements without |key| or type of |element[key]| is not string are smaller
// those whose |element[key]| is string.
+inline void sortJsonArrayByKey(nlohmann::json::array_t& array,
+ std::string_view key)
+{
+ std::ranges::sort(array, ODataObjectLess(key));
+}
+
+// Sort the JSON array by |element[key]|.
+// Elements without |key| or type of |element[key]| is not string are smaller
+// those whose |element[key]| is string.
inline void sortJsonArrayByOData(nlohmann::json::array_t& array)
{
- std::ranges::sort(array, ODataObjectLess());
+ std::ranges::sort(array, ODataObjectLess("@odata.id"));
}
// Returns the estimated size of the JSON value
diff --git a/redfish-core/lib/sensors.hpp b/redfish-core/lib/sensors.hpp
index a02eb60..f344220 100644
--- a/redfish-core/lib/sensors.hpp
+++ b/redfish-core/lib/sensors.hpp
@@ -709,30 +709,34 @@
for (const std::string& sensorGroup : sensorHeaders)
{
nlohmann::json::iterator entry = response.find(sensorGroup);
- if (entry != response.end())
+ if (entry == response.end())
{
- std::sort(entry->begin(), entry->end(),
- [](const nlohmann::json& c1, const nlohmann::json& c2) {
- return c1["Name"] < c2["Name"];
- });
+ continue;
+ }
+ nlohmann::json::array_t* arr =
+ entry->get_ptr<nlohmann::json::array_t*>();
+ if (arr == nullptr)
+ {
+ continue;
+ }
+ json_util::sortJsonArrayByKey(*arr, "Name");
- // add the index counts to the end of each entry
- size_t count = 0;
- for (nlohmann::json& sensorJson : *entry)
+ // add the index counts to the end of each entry
+ size_t count = 0;
+ for (nlohmann::json& sensorJson : *entry)
+ {
+ nlohmann::json::iterator odata = sensorJson.find("@odata.id");
+ if (odata == sensorJson.end())
{
- nlohmann::json::iterator odata = sensorJson.find("@odata.id");
- if (odata == sensorJson.end())
- {
- continue;
- }
- std::string* value = odata->get_ptr<std::string*>();
- if (value != nullptr)
- {
- *value += "/" + std::to_string(count);
- sensorJson["MemberId"] = std::to_string(count);
- count++;
- sensorsAsyncResp->updateUri(sensorJson["Name"], *value);
- }
+ continue;
+ }
+ std::string* value = odata->get_ptr<std::string*>();
+ if (value != nullptr)
+ {
+ *value += "/" + std::to_string(count);
+ sensorJson["MemberId"] = std::to_string(count);
+ count++;
+ sensorsAsyncResp->updateUri(sensorJson["Name"], *value);
}
}
}
diff --git a/test/redfish-core/include/utils/json_utils_test.cpp b/test/redfish-core/include/utils/json_utils_test.cpp
index 2238728..0f87647 100644
--- a/test/redfish-core/include/utils/json_utils_test.cpp
+++ b/test/redfish-core/include/utils/json_utils_test.cpp
@@ -429,7 +429,7 @@
R"({"@odata.id": 4})"_json));
}
-TEST(SortJsonArrayByKey, ElementMissingKeyReturnsFalseArrayIsPartlySorted)
+TEST(SortJsonArrayByOData, ElementMissingKeyReturnsFalseArrayIsPartlySorted)
{
nlohmann::json::array_t array =
R"([{"@odata.id" : "/redfish/v1/100"}, {"@odata.id": "/redfish/v1/1"}, {"@odata.id" : "/redfish/v1/20"}])"_json;
@@ -442,7 +442,7 @@
R"({"@odata.id" : "/redfish/v1/100"})"_json));
}
-TEST(SortJsonArrayByKey, SortedByStringValueOnSuccessArrayIsSorted)
+TEST(SortJsonArrayByOData, SortedByStringValueOnSuccessArrayIsSorted)
{
nlohmann::json::array_t array =
R"([{"@odata.id": "/redfish/v1/20"}, {"@odata.id" : "/redfish/v1"}, {"@odata.id" : "/redfish/v1/100"}])"_json;
@@ -452,6 +452,82 @@
R"({"@odata.id" : "/redfish/v1/20"})"_json,
R"({"@odata.id" : "/redfish/v1/100"})"_json));
}
+
+TEST(objectKeyCmp, PositiveCases)
+{
+ EXPECT_EQ(
+ 0, objectKeyCmp("@odata.id",
+ R"({"@odata.id": "/redfish/v1/1", "Name": "a"})"_json,
+ R"({"@odata.id": "/redfish/v1/1", "Name": "b"})"_json));
+ EXPECT_GT(
+ 0, objectKeyCmp("Name",
+ R"({"@odata.id": "/redfish/v1/1", "Name": "a"})"_json,
+ R"({"@odata.id": "/redfish/v1/1", "Name": "b"})"_json));
+
+ EXPECT_GT(
+ 0, objectKeyCmp("@odata.id",
+ R"({"@odata.id": "/redfish/v1/1", "Name": "b"})"_json,
+ R"({"@odata.id": "/redfish/v1/2", "Name": "b"})"_json));
+ EXPECT_EQ(
+ 0, objectKeyCmp("Name",
+ R"({"@odata.id": "/redfish/v1/1", "Name": "b"})"_json,
+ R"({"@odata.id": "/redfish/v1/2", "Name": "b"})"_json));
+
+ EXPECT_LT(0,
+ objectKeyCmp(
+ "@odata.id",
+ R"({"@odata.id": "/redfish/v1/p10/", "Name": "a1"})"_json,
+ R"({"@odata.id": "/redfish/v1/p1/", "Name": "a10"})"_json));
+ EXPECT_GT(0,
+ objectKeyCmp(
+ "Name",
+ R"({"@odata.id": "/redfish/v1/p10/", "Name": "a1"})"_json,
+ R"({"@odata.id": "/redfish/v1/p1/", "Name": "a10"})"_json));
+
+ nlohmann::json leftRequest =
+ R"({"Name": "fan1", "@odata.id": "/redfish/v1/Chassis/chassis2"})"_json;
+ nlohmann::json rightRequest =
+ R"({"Name": "fan2", "@odata.id": "/redfish/v1/Chassis/chassis1"})"_json;
+
+ EXPECT_GT(0, objectKeyCmp("Name", leftRequest, rightRequest));
+ EXPECT_LT(0, objectKeyCmp("@odata.id", leftRequest, rightRequest));
+ EXPECT_EQ(0, objectKeyCmp("DataSourceUri", leftRequest, rightRequest));
+}
+
+TEST(SortJsonArrayByKey, ElementMissingKeyReturnsFalseArrayIsPartlySorted)
+{
+ nlohmann::json::array_t array =
+ R"([{"@odata.id" : "/redfish/v1/100"}, {"Name" : "/redfish/v1/5"}, {"@odata.id": "/redfish/v1/1"}, {"@odata.id" : "/redfish/v1/20"}])"_json;
+ sortJsonArrayByKey(array, "@odata.id");
+ // Objects with other keys are always smaller than those with the specified
+ // key.
+ EXPECT_THAT(array,
+ ElementsAre(R"({"Name" : "/redfish/v1/5"})"_json,
+ R"({"@odata.id": "/redfish/v1/1"})"_json,
+ R"({"@odata.id" : "/redfish/v1/20"})"_json,
+ R"({"@odata.id" : "/redfish/v1/100"})"_json));
+}
+
+TEST(SortJsonArrayByKey, SortedByStringValueOnSuccessArrayIsSorted)
+{
+ nlohmann::json::array_t array =
+ R"([{"@odata.id": "/redfish/v1/20", "Name": "a"}, {"@odata.id" : "/redfish/v1", "Name": "c"}, {"@odata.id" : "/redfish/v1/100", "Name": "b"}])"_json;
+
+ sortJsonArrayByKey(array, "@odata.id");
+ EXPECT_THAT(
+ array,
+ ElementsAre(R"({"@odata.id": "/redfish/v1", "Name": "c"})"_json,
+ R"({"@odata.id": "/redfish/v1/20", "Name": "a"})"_json,
+ R"({"@odata.id": "/redfish/v1/100", "Name": "b"})"_json));
+
+ sortJsonArrayByKey(array, "Name");
+ EXPECT_THAT(
+ array,
+ ElementsAre(R"({"@odata.id": "/redfish/v1/20", "Name": "a"})"_json,
+ R"({"@odata.id": "/redfish/v1/100", "Name": "b"})"_json,
+ R"({"@odata.id": "/redfish/v1", "Name": "c"})"_json));
+}
+
TEST(GetEstimatedJsonSize, NumberIs8Bytpes)
{
EXPECT_EQ(getEstimatedJsonSize(nlohmann::json(123)), 8);