Refactor PCIeDeviceList to use getCollectionMembers

This commit refactors the code in the PCIeDeviceList function to use the
getCollectionMembers function for retrieving collection members.
Additionally, a new function getCollectionToKey() is added to
handle the retrieval of collection members with custom key name.

Tested: Validator passed

'''
Test1: Redfish query of PCI devices on a system that does not have
any PCIe devices

curl -k https://$bmc/redfish/v1/Systems/system/PCIeDevices
{
  "@odata.id": "/redfish/v1/Systems/system/PCIeDevices",
  "@odata.type": "#PCIeDeviceCollection.PCIeDeviceCollection",
  "Description": "Collection of PCIe Devices",
  "Members": [],
  "Members@odata.count": 0,
  "Name": "PCIe Device Collection"
}

Test2: Redfish query of PCIe devices on a system that has PCIe devices

curl -k https://$bmc/redfish/v1/Systems/system/PCIeDevices
{
  "@odata.id": "/redfish/v1/Systems/system/PCIeDevices",
  "@odata.type": "#PCIeDeviceCollection.PCIeDeviceCollection",
  "Description": "Collection of PCIe Devices",
  "Members": [
    {
      "@odata.id": "/redfish/v1/Systems/system/PCIeDevices/drive0"
    },
.......
    {
      "@odata.id": "/redfish/v1/Systems/system/PCIeDevices/pcie_card1"
    },
    {
      "@odata.id": "/redfish/v1/Systems/system/PCIeDevices/pcie_card2"
    },
.......
    {
      "@odata.id": "/redfish/v1/Systems/system/PCIeDevices/pcie_card12"
    }
  ],
  "Members@odata.count": 22,
  "Name": "PCIe Device Collection"
}

Test3: Redfish query of system with PCIe devices
curl -k https://$bmc/redfish/v1/Systems/system
{
  "@odata.id": "/redfish/v1/Systems/system",
  "@odata.type": "#ComputerSystem.v1_16_0.ComputerSystem",
  "Actions": {
    "#ComputerSystem.Reset": {
      "@Redfish.ActionInfo": "/redfish/v1/Systems/system/ResetActionInfo",
      "target": "/redfish/v1/Systems/system/Actions/ComputerSystem.Reset"
    }
  },
......
  "PCIeDevices": [
    {
      "@odata.id": "/redfish/v1/Systems/system/PCIeDevices/drive0"
    },
.......
    {
      "@odata.id": "/redfish/v1/Systems/system/PCIeDevices/pcie_card1"
    },
....
  ],
  "PCIeDevices@odata.count": 22,
  "PartNumber": "",
....
  "SubModel": "S0",
  "SystemType": "Physical"
}
'''

Change-Id: Icb38945a2c7bc5219ff3917fbbc8a9986c9c6155
Signed-off-by: Lakshmi Yadlapati <lakshmiy@us.ibm.com>
diff --git a/redfish-core/include/utils/collection.hpp b/redfish-core/include/utils/collection.hpp
index 7d28c0f..79e4fc0 100644
--- a/redfish-core/include/utils/collection.hpp
+++ b/redfish-core/include/utils/collection.hpp
@@ -22,13 +22,20 @@
 
 inline void handleCollectionMembers(
     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-    const boost::urls::url& collectionPath, const boost::system::error_code& ec,
+    const boost::urls::url& collectionPath,
+    const nlohmann::json::json_pointer& jsonKeyName,
+    const boost::system::error_code& ec,
     const dbus::utility::MapperGetSubTreePathsResponse& objects)
 {
+    nlohmann::json::json_pointer jsonCountKeyName = jsonKeyName;
+    std::string back = jsonCountKeyName.back();
+    jsonCountKeyName.pop_back();
+    jsonCountKeyName /= back + "@data.count";
+
     if (ec == boost::system::errc::io_error)
     {
-        asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
-        asyncResp->res.jsonValue["Members@odata.count"] = 0;
+        asyncResp->res.jsonValue[jsonKeyName] = nlohmann::json::array();
+        asyncResp->res.jsonValue[jsonCountKeyName] = 0;
         return;
     }
 
@@ -52,7 +59,7 @@
     }
     std::ranges::sort(pathNames, AlphanumLess<std::string>());
 
-    nlohmann::json& members = asyncResp->res.jsonValue["Members"];
+    nlohmann::json& members = asyncResp->res.jsonValue[jsonKeyName];
     members = nlohmann::json::array();
     for (const std::string& leaf : pathNames)
     {
@@ -62,11 +69,11 @@
         member["@odata.id"] = std::move(url);
         members.emplace_back(std::move(member));
     }
-    asyncResp->res.jsonValue["Members@odata.count"] = members.size();
+    asyncResp->res.jsonValue[jsonCountKeyName] = members.size();
 }
 
 /**
- * @brief Populate the collection "Members" from a GetSubTreePaths search of
+ * @brief Populate the collection members from a GetSubTreePaths search of
  *        inventory
  *
  * @param[i,o] asyncResp  Async response object
@@ -74,19 +81,32 @@
  *             Members Redfish Path
  * @param[i]   interfaces  List of interfaces to constrain the GetSubTree search
  * @param[in]  subtree     D-Bus base path to constrain search to.
+ * @param[in]  jsonKeyName Key name in which the collection members will be
+ *             stored.
  *
  * @return void
  */
 inline void
+    getCollectionToKey(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+                       const boost::urls::url& collectionPath,
+                       std::span<const std::string_view> interfaces,
+                       const std::string& subtree,
+                       const nlohmann::json::json_pointer& jsonKeyName)
+{
+    BMCWEB_LOG_DEBUG("Get collection members for: {}", collectionPath.buffer());
+    dbus::utility::getSubTreePaths(subtree, 0, interfaces,
+                                   std::bind_front(handleCollectionMembers,
+                                                   asyncResp, collectionPath,
+                                                   jsonKeyName));
+}
+inline void
     getCollectionMembers(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
                          const boost::urls::url& collectionPath,
                          std::span<const std::string_view> interfaces,
                          const std::string& subtree)
 {
-    BMCWEB_LOG_DEBUG("Get collection members for: {}", collectionPath.buffer());
-    dbus::utility::getSubTreePaths(
-        subtree, 0, interfaces,
-        std::bind_front(handleCollectionMembers, asyncResp, collectionPath));
+    getCollectionToKey(asyncResp, collectionPath, interfaces, subtree,
+                       nlohmann::json::json_pointer("/Members"));
 }
 
 } // namespace collection_util
diff --git a/redfish-core/include/utils/pcie_util.hpp b/redfish-core/include/utils/pcie_util.hpp
index 1a9d2bb..c685c3e 100644
--- a/redfish-core/include/utils/pcie_util.hpp
+++ b/redfish-core/include/utils/pcie_util.hpp
@@ -6,6 +6,7 @@
 #include "generated/enums/pcie_device.hpp"
 #include "generated/enums/pcie_slots.hpp"
 #include "http/utility.hpp"
+#include "utils/collection.hpp"
 
 #include <boost/system/error_code.hpp>
 #include <boost/url/format.hpp>
@@ -34,44 +35,16 @@
 
 inline void
     getPCIeDeviceList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-                      const std::string& name)
+                      const nlohmann::json::json_pointer& jsonKeyName)
 {
     static constexpr std::array<std::string_view, 1> pcieDeviceInterface = {
         "xyz.openbmc_project.Inventory.Item.PCIeDevice"};
+    const boost::urls::url pcieDeviceUrl =
+        boost::urls::url("/redfish/v1/Systems/system/PCIeDevices");
 
-    dbus::utility::getSubTreePaths(
-        "/xyz/openbmc_project/inventory", 0, pcieDeviceInterface,
-        [asyncResp, name](const boost::system::error_code& ec,
-                          const dbus::utility::MapperGetSubTreePathsResponse&
-                              pcieDevicePaths) {
-        if (ec)
-        {
-            BMCWEB_LOG_DEBUG("no PCIe device paths found ec: {}", ec.message());
-            // Not an error, system just doesn't have PCIe info
-            return;
-        }
-        nlohmann::json& pcieDeviceList = asyncResp->res.jsonValue[name];
-        pcieDeviceList = nlohmann::json::array();
-        for (const std::string& pcieDevicePath : pcieDevicePaths)
-        {
-            size_t devStart = pcieDevicePath.rfind('/');
-            if (devStart == std::string::npos)
-            {
-                continue;
-            }
-
-            std::string devName = pcieDevicePath.substr(devStart + 1);
-            if (devName.empty())
-            {
-                continue;
-            }
-            nlohmann::json::object_t pcieDevice;
-            pcieDevice["@odata.id"] = boost::urls::format(
-                "/redfish/v1/Systems/system/PCIeDevices/{}", devName);
-            pcieDeviceList.emplace_back(std::move(pcieDevice));
-        }
-        asyncResp->res.jsonValue[name + "@odata.count"] = pcieDeviceList.size();
-    });
+    collection_util::getCollectionToKey(
+        asyncResp, pcieDeviceUrl, pcieDeviceInterface,
+        "/xyz/openbmc_project/inventory", jsonKeyName);
 }
 
 inline std::optional<pcie_slots::SlotTypes>
diff --git a/redfish-core/lib/pcie.hpp b/redfish-core/lib/pcie.hpp
index 6bd74f9..e8da219 100644
--- a/redfish-core/lib/pcie.hpp
+++ b/redfish-core/lib/pcie.hpp
@@ -131,10 +131,9 @@
         "/redfish/v1/Systems/system/PCIeDevices";
     asyncResp->res.jsonValue["Name"] = "PCIe Device Collection";
     asyncResp->res.jsonValue["Description"] = "Collection of PCIe Devices";
-    asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
-    asyncResp->res.jsonValue["Members@odata.count"] = 0;
 
-    pcie_util::getPCIeDeviceList(asyncResp, "Members");
+    pcie_util::getPCIeDeviceList(asyncResp,
+                                 nlohmann::json::json_pointer("/Members"));
 }
 
 inline void requestRoutesSystemPCIeDeviceCollection(App& app)
diff --git a/redfish-core/lib/systems.hpp b/redfish-core/lib/systems.hpp
index e044c67..5f6cd87 100644
--- a/redfish-core/lib/systems.hpp
+++ b/redfish-core/lib/systems.hpp
@@ -3339,7 +3339,8 @@
     getBootProperties(asyncResp);
     getBootProgress(asyncResp);
     getBootProgressLastStateTime(asyncResp);
-    pcie_util::getPCIeDeviceList(asyncResp, "PCIeDevices");
+    pcie_util::getPCIeDeviceList(asyncResp,
+                                 nlohmann::json::json_pointer("/PCIeDevices"));
     getHostWatchdogTimer(asyncResp);
     getPowerRestorePolicy(asyncResp);
     getStopBootOnFault(asyncResp);