utils: Retrieval of managed objects of DBUS

This commit implements functionality to retrieve the managed object of a
specific DBUS service on a particular path.  Additionally implements a
function template for inventory objects which efficiently enables
retrieval of managed object for Inventory Manager.

Tested: Added unit test cases for checking the return value.

Change-Id: Ide652f843db1623bdacebf3e269e03895bbb7f1a
Signed-off-by: Riya Dixit <riyadixitagra@gmail.com>
diff --git a/common/test/mocked_utils.hpp b/common/test/mocked_utils.hpp
index 71bc6e7..e42677c 100644
--- a/common/test/mocked_utils.hpp
+++ b/common/test/mocked_utils.hpp
@@ -22,6 +22,35 @@
 } // namespace utils
 } // namespace pldm
 
+class GetManagedEmptyObject
+{
+  public:
+    static pldm::utils::ObjectValueTree getManagedObj(const char* /*service*/,
+                                                      const char* /*path*/)
+    {
+        return pldm::utils::ObjectValueTree{};
+    }
+};
+
+class GetManagedObject
+{
+  public:
+    static pldm::utils::ObjectValueTree getManagedObj(const char* /*service*/,
+                                                      const char* /*path*/)
+    {
+        return pldm::utils::ObjectValueTree{
+            {sdbusplus::message::object_path("/foo/bar"),
+             {{"foo.bar",
+               {{"Functional", true},
+                {"Enabled", true},
+                {"PrettyName", "System"},
+                {"Present", true},
+                {"SerialNumber", "abc123z"},
+                {"Model", "1234 - 00Z"},
+                {"SubModel", "S0"}}}}}};
+    }
+};
+
 class MockdBusHandler : public pldm::utils::DBusHandler
 {
   public:
diff --git a/common/test/pldm_utils_test.cpp b/common/test/pldm_utils_test.cpp
index 549e289..93cf335 100644
--- a/common/test/pldm_utils_test.cpp
+++ b/common/test/pldm_utils_test.cpp
@@ -1,4 +1,5 @@
 #include "common/utils.hpp"
+#include "mocked_utils.hpp"
 
 #include <libpldm/platform.h>
 
@@ -6,6 +7,28 @@
 
 using namespace pldm::utils;
 
+TEST(GetInventoryObjects, testForEmptyObject)
+{
+    ObjectValueTree result =
+        DBusHandler::getInventoryObjects<GetManagedEmptyObject>();
+    EXPECT_TRUE(result.empty());
+}
+
+TEST(GetInventoryObjects, testForObject)
+{
+    std::string path = "/foo/bar";
+    std::string service = "foo.bar";
+    auto result = DBusHandler::getInventoryObjects<GetManagedObject>();
+    EXPECT_EQ(result[path].begin()->first, service);
+    auto function =
+        std::get<bool>(result[path][service][std::string("Functional")]);
+    auto model =
+        std::get<std::string>(result[path][service][std::string("Model")]);
+    EXPECT_FALSE(result.empty());
+    EXPECT_TRUE(function);
+    EXPECT_EQ(model, std::string("1234 - 00Z"));
+}
+
 TEST(decodeDate, testGooduintToDate)
 {
     uint64_t data = 20191212115959;
diff --git a/common/utils.cpp b/common/utils.cpp
index 7e43b49..eee9f43 100644
--- a/common/utils.cpp
+++ b/common/utils.cpp
@@ -563,6 +563,16 @@
     return bus.call(method, dbusTimeout).unpack<PropertyValue>();
 }
 
+ObjectValueTree DBusHandler::getManagedObj(const char* service,
+                                           const char* rootPath)
+{
+    auto& bus = DBusHandler::getBus();
+    auto method = bus.new_method_call(service, rootPath,
+                                      "org.freedesktop.DBus.ObjectManager",
+                                      "GetManagedObjects");
+    return bus.call(method).unpack<ObjectValueTree>();
+}
+
 PropertyValue jsonEntryToDbusVal(std::string_view type,
                                  const nlohmann::json& value)
 {
diff --git a/common/utils.hpp b/common/utils.hpp
index e940265..2d3fb7f 100644
--- a/common/utils.hpp
+++ b/common/utils.hpp
@@ -14,6 +14,7 @@
 
 #include <nlohmann/json.hpp>
 #include <sdbusplus/server.hpp>
+#include <xyz/openbmc_project/Inventory/Manager/client.hpp>
 #include <xyz/openbmc_project/Logging/Entry/server.hpp>
 
 #include <deque>
@@ -182,8 +183,12 @@
     return bcd;
 }
 
+using inventoryManager =
+    sdbusplus::client::xyz::openbmc_project::inventory::Manager<>;
+
 constexpr auto dbusProperties = "org.freedesktop.DBus.Properties";
 constexpr auto mapperService = "xyz.openbmc_project.ObjectMapper";
+constexpr auto inventoryPath = "/xyz/openbmc_project/inventory";
 
 struct DBusMapping
 {
@@ -195,7 +200,8 @@
 
 using PropertyValue =
     std::variant<bool, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t,
-                 uint64_t, double, std::string, std::vector<std::string>>;
+                 uint64_t, double, std::string, std::vector<uint8_t>,
+                 std::vector<std::string>>;
 using DbusProp = std::string;
 using DbusChangedProps = std::map<DbusProp, PropertyValue>;
 using DBusInterfaceAdded = std::vector<
@@ -209,6 +215,7 @@
 using GetSubTreeResponse = std::vector<std::pair<ObjectPath, MapperServiceMap>>;
 using PropertyMap = std::map<std::string, PropertyValue>;
 using InterfaceMap = std::map<std::string, PropertyMap>;
+using ObjectValueTree = std::map<sdbusplus::message::object_path, InterfaceMap>;
 
 /**
  * @brief The interface for DBusHandler
@@ -328,6 +335,34 @@
      */
     void setDbusProperty(const DBusMapping& dBusMap,
                          const PropertyValue& value) const override;
+
+    /** @brief This function retrieves the properties of an object managed
+     *         by the specified D-Bus service located at the given object path.
+     *
+     *  @param[in] service - The D-Bus service providing the managed object
+     *  @param[in] value - The object path of the managed object
+     *
+     *  @return A hierarchical structure representing the properties of the
+     *          managed object.
+     *  @throw sdbusplus::exception::exception when it fails
+     */
+    static ObjectValueTree getManagedObj(const char* service, const char* path);
+
+    /** @brief Retrieve the inventory objects managed by a specified class.
+     *         The retrieved inventory objects are cached statically
+     *         and returned upon subsequent calls to this function.
+     *
+     *  @tparam ClassType - The class type that manages the inventory objects.
+     *
+     *  @return A reference to the cached inventory objects.
+     */
+    template <typename ClassType>
+    static auto& getInventoryObjects()
+    {
+        static ObjectValueTree object = ClassType::getManagedObj(
+            inventoryManager::interface, inventoryPath);
+        return object;
+    }
 };
 
 /** @brief Fetch parent D-Bus object based on pathname
diff --git a/libpldmresponder/fru.cpp b/libpldmresponder/fru.cpp
index 632489c..a9ef613 100644
--- a/libpldmresponder/fru.cpp
+++ b/libpldmresponder/fru.cpp
@@ -145,18 +145,12 @@
     }
 
     fru_parser::DBusLookupInfo dbusInfo;
-    // Read the all the inventory D-Bus objects
-    auto& bus = pldm::utils::DBusHandler::getBus();
-    dbus::ObjectValueTree objects;
 
     try
     {
         dbusInfo = parser.inventoryLookup();
-        auto method = bus.new_method_call(
-            std::get<0>(dbusInfo).c_str(), std::get<1>(dbusInfo).c_str(),
-            "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
-        auto reply = bus.call(method, dbusTimeout);
-        reply.read(objects);
+        objects = pldm::utils::DBusHandler::getInventoryObjects<
+            pldm::utils::DBusHandler>();
     }
     catch (const std::exception& e)
     {
diff --git a/libpldmresponder/fru.hpp b/libpldmresponder/fru.hpp
index 20bc509..40a1517 100644
--- a/libpldmresponder/fru.hpp
+++ b/libpldmresponder/fru.hpp
@@ -24,9 +24,9 @@
 namespace dbus
 {
 
-using Value =
-    std::variant<bool, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t,
-                 uint64_t, double, std::string, std::vector<uint8_t>>;
+using Value = std::variant<bool, uint8_t, int16_t, uint16_t, int32_t, uint32_t,
+                           int64_t, uint64_t, double, std::string,
+                           std::vector<uint8_t>, std::vector<std::string>>;
 using PropertyMap = std::map<Property, Value>;
 using InterfaceMap = std::map<Interface, PropertyMap>;
 using ObjectValueTree = std::map<sdbusplus::message::object_path, InterfaceMap>;
@@ -225,6 +225,7 @@
     pldm_entity_association_tree* entityTree;
     pldm_entity_association_tree* bmcEntityTree;
     pldm::responder::oem_fru::Handler* oemFruHandler;
+    dbus::ObjectValueTree objects;
 
     std::map<dbus::ObjectPath, pldm_entity_node*> objToEntityNode{};
 
diff --git a/oem/ibm/libpldmresponder/file_io_type_dump.cpp b/oem/ibm/libpldmresponder/file_io_type_dump.cpp
index f1c6bec..f3394a3 100644
--- a/oem/ibm/libpldmresponder/file_io_type_dump.cpp
+++ b/oem/ibm/libpldmresponder/file_io_type_dump.cpp
@@ -46,14 +46,11 @@
     static constexpr auto DUMP_MANAGER_BUSNAME =
         "xyz.openbmc_project.Dump.Manager";
     static constexpr auto DUMP_MANAGER_PATH = "/xyz/openbmc_project/dump";
-    static constexpr auto OBJECT_MANAGER_INTERFACE =
-        "org.freedesktop.DBus.ObjectManager";
-    auto& bus = pldm::utils::DBusHandler::getBus();
 
     // Stores the current resource dump entry path
     std::string curResDumpEntryPath{};
 
-    dbus::ObjectValueTree objects;
+    ObjectValueTree objects;
     // Select the dump entry interface for system dump or resource dump
     DumpEntryInterface dumpEntryIntf = systemDumpEntry;
     if ((dumpType == PLDM_FILE_TYPE_RESOURCE_DUMP) ||
@@ -64,12 +61,8 @@
 
     try
     {
-        auto method =
-            bus.new_method_call(DUMP_MANAGER_BUSNAME, DUMP_MANAGER_PATH,
-                                OBJECT_MANAGER_INTERFACE, "GetManagedObjects");
-
-        auto reply = bus.call(method, dbusTimeout);
-        reply.read(objects);
+        objects = pldm::utils::DBusHandler::getManagedObj(DUMP_MANAGER_BUSNAME,
+                                                          DUMP_MANAGER_PATH);
     }
     catch (const sdbusplus::exception_t& e)
     {
diff --git a/requester/mctp_endpoint_discovery.cpp b/requester/mctp_endpoint_discovery.cpp
index 1a8a6a9..a962228 100644
--- a/requester/mctp_endpoint_discovery.cpp
+++ b/requester/mctp_endpoint_discovery.cpp
@@ -20,15 +20,12 @@
                            "/xyz/openbmc_project/mctp"),
                        std::bind_front(&MctpDiscovery::dicoverEndpoints, this))
 {
-    dbus::ObjectValueTree objects;
+    pldm::utils::ObjectValueTree objects;
 
     try
     {
-        auto method = bus.new_method_call(
-            "xyz.openbmc_project.MCTP", "/xyz/openbmc_project/mctp",
-            "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
-        auto reply = bus.call(method, dbusTimeout);
-        reply.read(objects);
+        objects = pldm::utils::DBusHandler::getManagedObj(MCTPService,
+                                                          MCTPPath);
     }
     catch (const std::exception& e)
     {
diff --git a/requester/mctp_endpoint_discovery.hpp b/requester/mctp_endpoint_discovery.hpp
index 7fbe069..de3a6bd 100644
--- a/requester/mctp_endpoint_discovery.hpp
+++ b/requester/mctp_endpoint_discovery.hpp
@@ -7,6 +7,9 @@
 namespace pldm
 {
 
+constexpr auto MCTPService = "xyz.openbmc_project.MCTP";
+constexpr auto MCTPPath = "/xyz/openbmc_project/mctp";
+
 class MctpDiscovery
 {
   public: