$filter Query parameter support for nested keys added

Implemented the code to identify the '/' character in the
key and perform the level by level search

Testing :

Tested query parameter with path separated by / example

curl -k -u root:0penBmc https://<IP>/redfish/v1/Systems/
Baseboard/LogServices/FaultLog/Entries?$filter=CPER/Oem/
OEM/IpSignature eq 'DRAM-CHANNELS'

Results having 'DRAM-CHANNELS' in nested path "CPER/Oem/
OEM/IpSignature" are listed.

Change-Id: Ie6cf796026a29ec7a3e8a0366bbfd0c658d0ac7e
Signed-off-by: Chandramohan Harkude <chandramohan.harkude@gmail.com>
diff --git a/redfish-core/include/utils/json_utils.hpp b/redfish-core/include/utils/json_utils.hpp
index 966593d..c93174c 100644
--- a/redfish-core/include/utils/json_utils.hpp
+++ b/redfish-core/include/utils/json_utils.hpp
@@ -685,6 +685,41 @@
     return {std::move(*object)};
 }
 
+inline const nlohmann::json* findNestedKey(std::string_view key,
+                                           const nlohmann::json& value)
+{
+    size_t keysplitIndex = key.find('/');
+    std::string_view leftover;
+    nlohmann::json::const_iterator it;
+    if (keysplitIndex != std::string_view::npos)
+    {
+        const nlohmann::json::object_t* obj =
+            value.get_ptr<const nlohmann::json::object_t*>();
+        if (obj == nullptr || obj->empty())
+        {
+            BMCWEB_LOG_ERROR("Requested key wasn't an object");
+            return nullptr;
+        }
+
+        leftover = key.substr(keysplitIndex + 1);
+        std::string_view keypart = key.substr(0, keysplitIndex);
+        it = value.find(keypart);
+        if (it == value.end())
+        {
+            // Entry didn't have key
+            return nullptr;
+        }
+        return findNestedKey(leftover, it.value());
+    }
+
+    it = value.find(key);
+    if (it == value.end())
+    {
+        return nullptr;
+    }
+    return &*it;
+}
+
 template <typename... UnpackTypes>
 bool readJsonPatch(const crow::Request& req, crow::Response& res,
                    std::string_view key, UnpackTypes&&... in)
diff --git a/redfish-core/src/filter_expr_executor.cpp b/redfish-core/src/filter_expr_executor.cpp
index df22ec2..4d92ea1 100644
--- a/redfish-core/src/filter_expr_executor.cpp
+++ b/redfish-core/src/filter_expr_executor.cpp
@@ -5,6 +5,7 @@
 #include "filter_expr_parser_ast.hpp"
 #include "human_sort.hpp"
 #include "logging.hpp"
+#include "utils/json_utils.hpp"
 #include "utils/time_utils.hpp"
 
 #include <nlohmann/json.hpp>
@@ -129,26 +130,29 @@
 ValueVisitor::result_type
     ValueVisitor::operator()(const filter_ast::UnquotedString& x)
 {
-    // Future, handle paths with / in them
-    nlohmann::json::const_iterator entry = body.find(x);
-    if (entry == body.end())
+    // find key including paths with / in them
+    const nlohmann::json* it =
+        json_util::findNestedKey(static_cast<std::string_view>(x), body);
+    if (it == nullptr)
     {
         BMCWEB_LOG_ERROR("Key {} doesn't exist in output, cannot filter",
                          static_cast<std::string>(x));
         BMCWEB_LOG_DEBUG("Output {}", body.dump());
         return {};
     }
-    const double* dValue = entry->get_ptr<const double*>();
+
+    const nlohmann::json& entry = *it;
+    const double* dValue = entry.get_ptr<const double*>();
     if (dValue != nullptr)
     {
         return {*dValue};
     }
-    const int64_t* iValue = entry->get_ptr<const int64_t*>();
+    const int64_t* iValue = entry.get_ptr<const int64_t*>();
     if (iValue != nullptr)
     {
         return {*iValue};
     }
-    const std::string* strValue = entry->get_ptr<const std::string*>();
+    const std::string* strValue = entry.get_ptr<const std::string*>();
     if (strValue != nullptr)
     {
         if (DateTimeString::isDateTimeKey(x))
@@ -160,7 +164,7 @@
 
     BMCWEB_LOG_ERROR(
         "Type for key {} was {} which does not have a comparison operator",
-        static_cast<std::string>(x), static_cast<int>(entry->type()));
+        static_cast<std::string>(x), static_cast<int>(entry.type()));
     return {};
 }
 
diff --git a/test/redfish-core/include/filter_expr_executor_test.cpp b/test/redfish-core/include/filter_expr_executor_test.cpp
index cc38a1c..bebe668 100644
--- a/test/redfish-core/include/filter_expr_executor_test.cpp
+++ b/test/redfish-core/include/filter_expr_executor_test.cpp
@@ -251,4 +251,21 @@
     filterFalse("'2021-11-30T22:41:35.124+00:00' le Created", members);
 }
 
+TEST(FilterParser, NestedProperty)
+{
+    const nlohmann::json members = R"({"Members": [{"Oem": {
+        "OEM": {
+          "@odata.type": "#OEMLogEntry.v1_1_0.OEMLogEntry",
+          "Key": "Switch_1",
+          "ErrorId": "SWITCH_EC_STRAP_MISMATCH"
+        }
+      }}]})"_json;
+    // Forward true conditions
+    filterTrue("Oem/OEM/ErrorId eq 'SWITCH_EC_STRAP_MISMATCH'", members);
+    filterTrue("Oem/OEM/Key eq 'Switch_1'", members);
+    filterFalse("Oem/OEM/ErrorId eq 'EC_STRAP_MISMATCH'", members);
+    filterFalse("Oem/OEM/ErrorId eq 'CX7_EC_STRAP_MISMATCH'", members);
+    filterFalse("Oem/OEM/ErrorId ne 'SWITCH_EC_STRAP_MISMATCH'", members);
+}
+
 } // namespace redfish