Refactor SystemPCIeDevice method

Move SystemPCIeDevice to a separate method, and refactor the code.
Validate the PCIe device path.

Tested: Validator passed

```# curl -k https://$bmc/redfish/v1/Systems/system/PCIeDevices/pcie_card7
{
  "@odata.id": "/redfish/v1/Systems/system/PCIeDevices/pcie_card7",
  "@odata.type": "#PCIeDevice.v1_9_0.PCIeDevice",
  "Id": "pcie_card7",
  "Name": "PCIe Device",
  "PCIeInterface": {
    "LanesInUse": 4,
    "PCIeType": "Gen1"
  }
}
```

Change-Id: Ib9bd3ff37293b64adbf9987c3d932882befd21d4
Signed-off-by: Lakshmi Yadlapati <lakshmiy@us.ibm.com>
diff --git a/redfish-core/lib/pcie.hpp b/redfish-core/lib/pcie.hpp
index 7cea840..969e1fa 100644
--- a/redfish-core/lib/pcie.hpp
+++ b/redfish-core/lib/pcie.hpp
@@ -36,6 +36,69 @@
 static constexpr char const* pcieDeviceInterface =
     "xyz.openbmc_project.Inventory.Item.PCIeDevice";
 
+static inline void handlePCIeDevicePath(
+    const std::string& pcieDeviceId,
+    const std::shared_ptr<bmcweb::AsyncResp>& aResp,
+    const dbus::utility::MapperGetSubTreePathsResponse& pcieDevicePaths,
+    const std::function<void(const std::string& pcieDevicePath,
+                             const std::string& service)>& callback)
+
+{
+    for (const std::string& pcieDevicePath : pcieDevicePaths)
+    {
+        std::string pciecDeviceName =
+            sdbusplus::message::object_path(pcieDevicePath).filename();
+        if (pciecDeviceName.empty() || pciecDeviceName != pcieDeviceId)
+        {
+            continue;
+        }
+
+        dbus::utility::getDbusObject(
+            pcieDevicePath, {},
+            [pcieDevicePath, aResp,
+             callback](const boost::system::error_code& ec,
+                       const dbus::utility::MapperGetObject& object) {
+            if (ec || object.empty())
+            {
+                BMCWEB_LOG_ERROR << "DBUS response error " << ec;
+                messages::internalError(aResp->res);
+                return;
+            }
+            callback(pcieDevicePath, object.begin()->first);
+            });
+        return;
+    }
+
+    BMCWEB_LOG_WARNING << "PCIe Device not found";
+    messages::resourceNotFound(aResp->res, "PCIeDevice", pcieDeviceId);
+}
+
+static inline void getValidPCIeDevicePath(
+    const std::string& pcieDeviceId,
+    const std::shared_ptr<bmcweb::AsyncResp>& aResp,
+    const std::function<void(const std::string& pcieDevicePath,
+                             const std::string& service)>& callback)
+{
+    constexpr std::array<std::string_view, 1> interfaces{
+        "xyz.openbmc_project.Inventory.Item.PCIeDevice"};
+
+    dbus::utility::getSubTreePaths(
+        "/xyz/openbmc_project/inventory", 0, interfaces,
+        [pcieDeviceId, aResp,
+         callback](const boost::system::error_code& ec,
+                   const dbus::utility::MapperGetSubTreePathsResponse&
+                       pcieDevicePaths) {
+        if (ec)
+        {
+            BMCWEB_LOG_ERROR << "D-Bus response error on GetSubTree " << ec;
+            messages::internalError(aResp->res);
+            return;
+        }
+        handlePCIeDevicePath(pcieDeviceId, aResp, pcieDevicePaths, callback);
+        return;
+        });
+}
+
 static inline void
     getPCIeDeviceList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
                       const std::string& name)
@@ -90,6 +153,7 @@
         messages::resourceNotFound(aResp->res, "ComputerSystem", systemName);
         return;
     }
+
     aResp->res.addHeader(boost::beast::http::field::link,
                          "</redfish/v1/JsonSchemas/PCIeDeviceCollection/"
                          "PCIeDeviceCollection.json>; rel=describedby");
@@ -159,116 +223,134 @@
     return std::nullopt;
 }
 
+inline void addPCIeDeviceProperties(
+    crow::Response& resp,
+    const dbus::utility::DBusPropertiesMap& pcieDevProperties)
+{
+    const std::string* manufacturer = nullptr;
+    const std::string* deviceType = nullptr;
+    const std::string* generationInUse = nullptr;
+    const int64_t* lanesInUse = nullptr;
+
+    const bool success = sdbusplus::unpackPropertiesNoThrow(
+        dbus_utils::UnpackErrorPrinter(), pcieDevProperties, "DeviceType",
+        deviceType, "GenerationInUse", generationInUse, "LanesInUse",
+        lanesInUse, "Manufacturer", manufacturer);
+
+    if (!success)
+    {
+        messages::internalError(resp);
+        return;
+    }
+
+    if (deviceType != nullptr && !deviceType->empty())
+    {
+        resp.jsonValue["PCIeInterface"]["DeviceType"] = *deviceType;
+    }
+
+    if (generationInUse != nullptr)
+    {
+        std::optional<pcie_device::PCIeTypes> redfishGenerationInUse =
+            redfishPcieGenerationFromDbus(*generationInUse);
+
+        if (!redfishGenerationInUse)
+        {
+            messages::internalError(resp);
+            return;
+        }
+        if (*redfishGenerationInUse != pcie_device::PCIeTypes::Invalid)
+        {
+            resp.jsonValue["PCIeInterface"]["PCIeType"] =
+                *redfishGenerationInUse;
+        }
+    }
+
+    // The default value of LanesInUse is 0, and the field will be
+    // left as off if it is a default value.
+    if (lanesInUse != nullptr && *lanesInUse != 0)
+    {
+        resp.jsonValue["PCIeInterface"]["LanesInUse"] = *lanesInUse;
+    }
+
+    if (manufacturer != nullptr)
+    {
+        resp.jsonValue["PCIeInterface"]["Manufacturer"] = *manufacturer;
+    }
+}
+
+inline void getPCIeDeviceProperties(
+    const std::shared_ptr<bmcweb::AsyncResp>& aResp,
+    const std::string& pcieDevicePath, const std::string& service,
+    const std::function<void(
+        const dbus::utility::DBusPropertiesMap& pcieDevProperties)>&& callback)
+{
+    sdbusplus::asio::getAllProperties(
+        *crow::connections::systemBus, service, pcieDevicePath,
+        "xyz.openbmc_project.Inventory.Item.PCIeDevice",
+        [aResp,
+         callback](const boost::system::error_code& ec,
+                   const dbus::utility::DBusPropertiesMap& pcieDevProperties) {
+        if (ec)
+        {
+            if (ec.value() != EBADR)
+            {
+                BMCWEB_LOG_ERROR << "DBUS response error for Properties";
+                messages::internalError(aResp->res);
+            }
+            return;
+        }
+        callback(pcieDevProperties);
+        });
+}
+
+inline void addPCIeDeviceCommonProperties(
+    const std::shared_ptr<bmcweb::AsyncResp>& aResp,
+    const std::string& pcieDeviceId)
+{
+    aResp->res.addHeader(
+        boost::beast::http::field::link,
+        "</redfish/v1/JsonSchemas/PCIeDevice/PCIeDevice.json>; rel=describedby");
+    aResp->res.jsonValue["@odata.type"] = "#PCIeDevice.v1_9_0.PCIeDevice";
+    aResp->res.jsonValue["@odata.id"] = crow::utility::urlFromPieces(
+        "redfish", "v1", "Systems", "system", "PCIeDevices", pcieDeviceId);
+    aResp->res.jsonValue["Name"] = "PCIe Device";
+    aResp->res.jsonValue["Id"] = pcieDeviceId;
+}
+
+inline void handlePCIeDeviceGet(App& app, const crow::Request& req,
+                                const std::shared_ptr<bmcweb::AsyncResp>& aResp,
+                                const std::string& systemName,
+                                const std::string& pcieDeviceId)
+{
+    if (!redfish::setUpRedfishRoute(app, req, aResp))
+    {
+        return;
+    }
+    if (systemName != "system")
+    {
+        messages::resourceNotFound(aResp->res, "ComputerSystem", systemName);
+        return;
+    }
+
+    getValidPCIeDevicePath(
+        pcieDeviceId, aResp,
+        [aResp, pcieDeviceId](const std::string& pcieDevicePath,
+                              const std::string& service) {
+        addPCIeDeviceCommonProperties(aResp, pcieDeviceId);
+        getPCIeDeviceProperties(
+            aResp, pcieDevicePath, service,
+            [aResp](const dbus::utility::DBusPropertiesMap& pcieDevProperties) {
+            addPCIeDeviceProperties(aResp->res, pcieDevProperties);
+            });
+        });
+}
+
 inline void requestRoutesSystemPCIeDevice(App& app)
 {
     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/PCIeDevices/<str>/")
         .privileges(redfish::privileges::getPCIeDevice)
         .methods(boost::beast::http::verb::get)(
-            [&app](const crow::Request& req,
-                   const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-                   const std::string& systemName, const std::string& device) {
-        if (!redfish::setUpRedfishRoute(app, req, asyncResp))
-        {
-            return;
-        }
-        if (systemName != "system")
-        {
-            messages::resourceNotFound(asyncResp->res, "ComputerSystem",
-                                       systemName);
-            return;
-        }
-
-        auto getPCIeDeviceCallback =
-            [asyncResp, device](
-                const boost::system::error_code& ec,
-                const dbus::utility::DBusPropertiesMap& pcieDevProperties) {
-            if (ec)
-            {
-                BMCWEB_LOG_DEBUG
-                    << "failed to get PCIe Device properties ec: " << ec.value()
-                    << ": " << ec.message();
-                if (ec.value() ==
-                    boost::system::linux_error::bad_request_descriptor)
-                {
-                    messages::resourceNotFound(asyncResp->res, "PCIeDevice",
-                                               device);
-                }
-                else
-                {
-                    messages::internalError(asyncResp->res);
-                }
-                return;
-            }
-
-            const std::string* manufacturer = nullptr;
-            const std::string* deviceType = nullptr;
-            const std::string* generationInUse = nullptr;
-            const size_t* lanesInUse = nullptr;
-
-            const bool success = sdbusplus::unpackPropertiesNoThrow(
-                dbus_utils::UnpackErrorPrinter(), pcieDevProperties,
-                "Manufacturer", manufacturer, "DeviceType", deviceType,
-                "LanesInUse", lanesInUse, "GenerationInUse", generationInUse);
-
-            if (!success)
-            {
-                messages::internalError(asyncResp->res);
-                return;
-            }
-
-            // The default value of LanesInUse is 0, and the field will be
-            // left as off if it is a default value.
-            if (lanesInUse != nullptr && *lanesInUse != 0)
-            {
-                asyncResp->res.jsonValue["PCIeInterface"]["LanesInUse"] =
-                    *lanesInUse;
-            }
-
-            if (generationInUse != nullptr)
-            {
-                std::optional<pcie_device::PCIeTypes> redfishGenerationInUse =
-                    redfishPcieGenerationFromDbus(*generationInUse);
-                if (!redfishGenerationInUse)
-                {
-                    messages::internalError(asyncResp->res);
-                    return;
-                }
-                if (*redfishGenerationInUse != pcie_device::PCIeTypes::Invalid)
-                {
-                    asyncResp->res.jsonValue["PCIeInterface"]["PCIeType"] =
-                        *redfishGenerationInUse;
-                }
-            }
-
-            if (manufacturer != nullptr)
-            {
-                asyncResp->res.jsonValue["Manufacturer"] = *manufacturer;
-            }
-
-            if (deviceType != nullptr)
-            {
-                asyncResp->res.jsonValue["DeviceType"] = *deviceType;
-            }
-
-            asyncResp->res.jsonValue["@odata.type"] =
-                "#PCIeDevice.v1_4_0.PCIeDevice";
-            asyncResp->res.jsonValue["@odata.id"] =
-                crow::utility::urlFromPieces("redfish", "v1", "Systems",
-                                             "system", "PCIeDevices", device);
-            asyncResp->res.jsonValue["Name"] = "PCIe Device";
-            asyncResp->res.jsonValue["Id"] = device;
-
-            asyncResp->res.jsonValue["PCIeFunctions"]["@odata.id"] =
-                crow::utility::urlFromPieces("redfish", "v1", "Systems",
-                                             "system", "PCIeDevices", device,
-                                             "PCIeFunctions");
-        };
-        std::string escapedPath = std::string(pciePath) + "/" + device;
-        dbus::utility::escapePathForDbus(escapedPath);
-        sdbusplus::asio::getAllProperties(
-            *crow::connections::systemBus, pcieService, escapedPath,
-            pcieDeviceInterface, std::move(getPCIeDeviceCallback));
-        });
+            std::bind_front(handlePCIeDeviceGet, std::ref(app)));
 }
 
 inline void requestRoutesSystemPCIeFunctionCollection(App& app)