Refactor SystemPCIeFunction method

Move SystemPCIeFunction to a separate method, and refactor the code.
Validate the PCIe device path and add link header.

Tested: Validator passed

```
{
  "@odata.id": "/redfish/v1/Systems/system/PCIeDevices/pcie_card7/PCIeFunctions/0",
  "@odata.type": "#PCIeFunction.v1_2_3.PCIeFunction",
  "ClassCode": "0x020000",
  "DeviceId": "0x1657",
  "FunctionId": 0,
  "Id": "0",
  "Links": {
    "PCIeDevice": {
      "@odata.id": "/redfish/v1/Systems/system/PCIeDevices/pcie_card7"
    }
  },
  "Name": "PCIe Function",
  "RevisionId": "0x01",
  "SubsystemId": "0x0420",
  "SubsystemVendorId": "0x1014",
  "VendorId": "0x14e4"
}
```

Change-Id: I99add03210983a255a615512b6137d04982f83a5
Signed-off-by: Lakshmi Yadlapati <lakshmiy@us.ibm.com>
diff --git a/redfish-core/lib/pcie.hpp b/redfish-core/lib/pcie.hpp
index 4bf5626..f38ab51 100644
--- a/redfish-core/lib/pcie.hpp
+++ b/redfish-core/lib/pcie.hpp
@@ -442,6 +442,130 @@
             std::bind_front(handlePCIeFunctionCollectionGet, std::ref(app)));
 }
 
+inline bool validatePCIeFunctionId(
+    const std::string& pcieFunctionId,
+    const dbus::utility::DBusPropertiesMap& pcieDevProperties)
+{
+    std::string functionName = "Function" + pcieFunctionId;
+    std::string devIDProperty = functionName + "DeviceId";
+
+    const std::string* devIdProperty = nullptr;
+    for (const auto& property : pcieDevProperties)
+    {
+        if (property.first == devIDProperty)
+        {
+            devIdProperty = std::get_if<std::string>(&property.second);
+            break;
+        }
+    }
+    return (devIdProperty != nullptr && !devIdProperty->empty());
+}
+
+inline void addPCIeFunctionProperties(
+    crow::Response& resp, const std::string& pcieFunctionId,
+    const dbus::utility::DBusPropertiesMap& pcieDevProperties)
+{
+    std::string functionName = "Function" + pcieFunctionId;
+    if (!validatePCIeFunctionId(pcieFunctionId, pcieDevProperties))
+    {
+        messages::resourceNotFound(resp, "PCIeFunction", pcieFunctionId);
+        return;
+    }
+    for (const auto& property : pcieDevProperties)
+    {
+        const std::string* strProperty =
+            std::get_if<std::string>(&property.second);
+
+        if (property.first == functionName + "DeviceId")
+        {
+            resp.jsonValue["DeviceId"] = *strProperty;
+        }
+        if (property.first == functionName + "VendorId")
+        {
+            resp.jsonValue["VendorId"] = *strProperty;
+        }
+        // TODO: FunctionType and DeviceClass are Redfish enums. The D-Bus
+        // property strings should be mapped correctly to ensure these
+        // strings are Redfish enum values. For now just check for empty.
+        if (property.first == functionName + "FunctionType")
+        {
+            if (!strProperty->empty())
+            {
+                resp.jsonValue["FunctionType"] = *strProperty;
+            }
+        }
+        if (property.first == functionName + "DeviceClass")
+        {
+            if (!strProperty->empty())
+            {
+                resp.jsonValue["DeviceClass"] = *strProperty;
+            }
+        }
+        if (property.first == functionName + "ClassCode")
+        {
+            resp.jsonValue["ClassCode"] = *strProperty;
+        }
+        if (property.first == functionName + "RevisionId")
+        {
+            resp.jsonValue["RevisionId"] = *strProperty;
+        }
+        if (property.first == functionName + "SubsystemId")
+        {
+            resp.jsonValue["SubsystemId"] = *strProperty;
+        }
+        if (property.first == functionName + "SubsystemVendorId")
+        {
+            resp.jsonValue["SubsystemVendorId"] = *strProperty;
+        }
+    }
+}
+
+inline void addPCIeFunctionCommonProperties(crow::Response& resp,
+                                            const std::string& pcieDeviceId,
+                                            const std::string& pcieFunctionId)
+{
+    resp.addHeader(
+        boost::beast::http::field::link,
+        "</redfish/v1/JsonSchemas/PCIeFunction/PCIeFunction.json>; rel=describedby");
+    resp.jsonValue["@odata.type"] = "#PCIeFunction.v1_2_3.PCIeFunction";
+    resp.jsonValue["@odata.id"] = crow::utility::urlFromPieces(
+        "redfish", "v1", "Systems", "system", "PCIeDevices", pcieDeviceId,
+        "PCIeFunctions", pcieFunctionId);
+    resp.jsonValue["Name"] = "PCIe Function";
+    resp.jsonValue["Id"] = pcieFunctionId;
+    resp.jsonValue["FunctionId"] = std::stoi(pcieFunctionId);
+    resp.jsonValue["Links"]["PCIeDevice"]["@odata.id"] =
+        crow::utility::urlFromPieces("redfish", "v1", "Systems", "system",
+                                     "PCIeDevices", pcieDeviceId);
+}
+
+inline void
+    handlePCIeFunctionGet(App& app, const crow::Request& req,
+                          const std::shared_ptr<bmcweb::AsyncResp>& aResp,
+                          const std::string& pcieDeviceId,
+                          const std::string& pcieFunctionId)
+{
+    if (!redfish::setUpRedfishRoute(app, req, aResp))
+    {
+        return;
+    }
+
+    getValidPCIeDevicePath(
+        pcieDeviceId, aResp,
+        [aResp, pcieDeviceId, pcieFunctionId](const std::string& pcieDevicePath,
+                                              const std::string& service) {
+        getPCIeDeviceProperties(
+            aResp, pcieDevicePath, service,
+            [aResp, pcieDeviceId, pcieFunctionId](
+                const dbus::utility::DBusPropertiesMap& pcieDevProperties) {
+            addPCIeFunctionCommonProperties(aResp->res, pcieDeviceId,
+                                            pcieFunctionId);
+            addPCIeFunctionProperties(aResp->res, pcieFunctionId,
+                                      pcieDevProperties);
+            });
+        });
+}
+
 inline void requestRoutesSystemPCIeFunction(App& app)
 {
     BMCWEB_ROUTE(
@@ -449,114 +573,7 @@
         "/redfish/v1/Systems/system/PCIeDevices/<str>/PCIeFunctions/<str>/")
         .privileges(redfish::privileges::getPCIeFunction)
         .methods(boost::beast::http::verb::get)(
-            [&app](const crow::Request& req,
-                   const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-                   const std::string& device, const std::string& function) {
-        if (!redfish::setUpRedfishRoute(app, req, asyncResp))
-        {
-            return;
-        }
-        auto getPCIeDeviceCallback =
-            [asyncResp, device, function](
-                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;
-            }
-
-            // Check if this function exists by looking for a device
-            // ID
-            std::string functionName = "Function" + function;
-            std::string devIDProperty = functionName + "DeviceId";
-
-            const std::string* devIdProperty = nullptr;
-            for (const auto& property : pcieDevProperties)
-            {
-                if (property.first == devIDProperty)
-                {
-                    devIdProperty = std::get_if<std::string>(&property.second);
-                    continue;
-                }
-            }
-            if (devIdProperty == nullptr || devIdProperty->empty())
-            {
-                messages::resourceNotFound(asyncResp->res, "PCIeFunction",
-                                           function);
-                return;
-            }
-
-            asyncResp->res.jsonValue["@odata.type"] =
-                "#PCIeFunction.v1_2_0.PCIeFunction";
-            asyncResp->res.jsonValue["@odata.id"] =
-                crow::utility::urlFromPieces("redfish", "v1", "Systems",
-                                             "system", "PCIeDevices", device,
-                                             "PCIeFunctions", function);
-            asyncResp->res.jsonValue["Name"] = "PCIe Function";
-            asyncResp->res.jsonValue["Id"] = function;
-            asyncResp->res.jsonValue["FunctionId"] = std::stoi(function);
-            asyncResp->res.jsonValue["Links"]["PCIeDevice"]["@odata.id"] =
-                crow::utility::urlFromPieces("redfish", "v1", "Systems",
-                                             "system", "PCIeDevices", device);
-
-            for (const auto& property : pcieDevProperties)
-            {
-                const std::string* strProperty =
-                    std::get_if<std::string>(&property.second);
-                if (property.first == functionName + "DeviceId")
-                {
-                    asyncResp->res.jsonValue["DeviceId"] = *strProperty;
-                }
-                if (property.first == functionName + "VendorId")
-                {
-                    asyncResp->res.jsonValue["VendorId"] = *strProperty;
-                }
-                if (property.first == functionName + "FunctionType")
-                {
-                    asyncResp->res.jsonValue["FunctionType"] = *strProperty;
-                }
-                if (property.first == functionName + "DeviceClass")
-                {
-                    asyncResp->res.jsonValue["DeviceClass"] = *strProperty;
-                }
-                if (property.first == functionName + "ClassCode")
-                {
-                    asyncResp->res.jsonValue["ClassCode"] = *strProperty;
-                }
-                if (property.first == functionName + "RevisionId")
-                {
-                    asyncResp->res.jsonValue["RevisionId"] = *strProperty;
-                }
-                if (property.first == functionName + "SubsystemId")
-                {
-                    asyncResp->res.jsonValue["SubsystemId"] = *strProperty;
-                }
-                if (property.first == functionName + "SubsystemVendorId")
-                {
-                    asyncResp->res.jsonValue["SubsystemVendorId"] =
-                        *strProperty;
-                }
-            }
-        };
-        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(handlePCIeFunctionGet, std::ref(app)));
 }
 
 } // namespace redfish