chassis: replace lambda with inline functions

It has been a convention that request route functions take inline
functions instead of lambdas. The benifets include less indents,
beging more readable + unit test-able (take a look at the unit test that
this commit adds for example).

This commit also fixed neccessary headers to make the test compile. The
headers of the unit test source is a complete list. But headers of the
core codes are not complete. These header clean up will be done in a
separate effort once https://gerrit.openbmc.org/c/openbmc/bmcweb/+/55138
is submitted.

Tested:
1. no service validator errors on real hardware.

Signed-off-by: Nan Zhou <nanzhoumails@gmail.com>
Change-Id: I4b23ba54707cea947b5db771c72aa64899041511
diff --git a/redfish-core/lib/chassis.hpp b/redfish-core/lib/chassis.hpp
index cfa1832..7982281 100644
--- a/redfish-core/lib/chassis.hpp
+++ b/redfish-core/lib/chassis.hpp
@@ -17,6 +17,7 @@
 
 #include "health.hpp"
 #include "led.hpp"
+#include "utils/json_utils.hpp"
 
 #include <app.hpp>
 #include <dbus_utility.hpp>
@@ -131,6 +132,25 @@
         std::array<const char*, 1>{"xyz.openbmc_project.Chassis.Intrusion"});
 }
 
+inline void handleChassisCollectionGet(
+    App& app, const crow::Request& req,
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
+{
+    if (!redfish::setUpRedfishRoute(app, req, asyncResp))
+    {
+        return;
+    }
+    asyncResp->res.jsonValue["@odata.type"] =
+        "#ChassisCollection.ChassisCollection";
+    asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Chassis";
+    asyncResp->res.jsonValue["Name"] = "Chassis Collection";
+
+    collection_util::getCollectionMembers(
+        asyncResp, "/redfish/v1/Chassis",
+        {"xyz.openbmc_project.Inventory.Item.Board",
+         "xyz.openbmc_project.Inventory.Item.Chassis"});
+}
+
 /**
  * ChassisCollection derived class for delivering Chassis Collection Schema
  *  Functions triggers appropriate requests on DBus
@@ -140,22 +160,7 @@
     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/")
         .privileges(redfish::privileges::getChassisCollection)
         .methods(boost::beast::http::verb::get)(
-            [&app](const crow::Request& req,
-                   const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
-        if (!redfish::setUpRedfishRoute(app, req, asyncResp))
-        {
-            return;
-        }
-        asyncResp->res.jsonValue["@odata.type"] =
-            "#ChassisCollection.ChassisCollection";
-        asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Chassis";
-        asyncResp->res.jsonValue["Name"] = "Chassis Collection";
-
-        collection_util::getCollectionMembers(
-            asyncResp, "/redfish/v1/Chassis",
-            {"xyz.openbmc_project.Inventory.Item.Board",
-             "xyz.openbmc_project.Inventory.Item.Chassis"});
-        });
+            std::bind_front(handleChassisCollectionGet, std::ref(app)));
 }
 
 inline void
@@ -199,6 +204,363 @@
         });
 }
 
+inline void
+    handleChassisGet(App& app, const crow::Request& req,
+                     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+                     const std::string& chassisId)
+{
+    if (!redfish::setUpRedfishRoute(app, req, asyncResp))
+    {
+        return;
+    }
+    const std::array<const char*, 2> interfaces = {
+        "xyz.openbmc_project.Inventory.Item.Board",
+        "xyz.openbmc_project.Inventory.Item.Chassis"};
+
+    crow::connections::systemBus->async_method_call(
+        [asyncResp, chassisId(std::string(chassisId))](
+            const boost::system::error_code ec,
+            const dbus::utility::MapperGetSubTreeResponse& subtree) {
+        if (ec)
+        {
+            messages::internalError(asyncResp->res);
+            return;
+        }
+        // Iterate over all retrieved ObjectPaths.
+        for (const std::pair<
+                 std::string,
+                 std::vector<std::pair<std::string, std::vector<std::string>>>>&
+                 object : subtree)
+        {
+            const std::string& path = object.first;
+            const std::vector<std::pair<std::string, std::vector<std::string>>>&
+                connectionNames = object.second;
+
+            sdbusplus::message::object_path objPath(path);
+            if (objPath.filename() != chassisId)
+            {
+                continue;
+            }
+
+            auto health = std::make_shared<HealthPopulate>(asyncResp);
+
+            sdbusplus::asio::getProperty<std::vector<std::string>>(
+                *crow::connections::systemBus,
+                "xyz.openbmc_project.ObjectMapper", path + "/all_sensors",
+                "xyz.openbmc_project.Association", "endpoints",
+                [health](const boost::system::error_code ec2,
+                         const std::vector<std::string>& resp) {
+                if (ec2)
+                {
+                    return; // no sensors = no failures
+                }
+                health->inventory = resp;
+                });
+
+            health->populate();
+
+            if (connectionNames.empty())
+            {
+                BMCWEB_LOG_ERROR << "Got 0 Connection names";
+                continue;
+            }
+
+            asyncResp->res.jsonValue["@odata.type"] =
+                "#Chassis.v1_16_0.Chassis";
+            asyncResp->res.jsonValue["@odata.id"] =
+                "/redfish/v1/Chassis/" + chassisId;
+            asyncResp->res.jsonValue["Name"] = "Chassis Collection";
+            asyncResp->res.jsonValue["ChassisType"] = "RackMount";
+            asyncResp->res.jsonValue["Actions"]["#Chassis.Reset"]["target"] =
+                "/redfish/v1/Chassis/" + chassisId + "/Actions/Chassis.Reset";
+            asyncResp->res
+                .jsonValue["Actions"]["#Chassis.Reset"]["@Redfish.ActionInfo"] =
+                "/redfish/v1/Chassis/" + chassisId + "/ResetActionInfo";
+            asyncResp->res.jsonValue["PCIeDevices"]["@odata.id"] =
+                "/redfish/v1/Systems/system/PCIeDevices";
+
+            sdbusplus::asio::getProperty<std::vector<std::string>>(
+                *crow::connections::systemBus,
+                "xyz.openbmc_project.ObjectMapper", path + "/drive",
+                "xyz.openbmc_project.Association", "endpoints",
+                [asyncResp, chassisId](const boost::system::error_code ec3,
+                                       const std::vector<std::string>& resp) {
+                if (ec3 || resp.empty())
+                {
+                    return; // no drives = no failures
+                }
+
+                nlohmann::json reference;
+                reference["odata.id"] = crow::utility::urlFromPieces(
+                    "redfish", "v1", "Chassis", chassisId, "Drives");
+                asyncResp->res.jsonValue["Drives"] = std::move(reference);
+                });
+
+            const std::string& connectionName = connectionNames[0].first;
+
+            const std::vector<std::string>& interfaces2 =
+                connectionNames[0].second;
+            const std::array<const char*, 2> hasIndicatorLed = {
+                "xyz.openbmc_project.Inventory.Item.Panel",
+                "xyz.openbmc_project.Inventory.Item.Board.Motherboard"};
+
+            const std::string assetTagInterface =
+                "xyz.openbmc_project.Inventory.Decorator.AssetTag";
+            if (std::find(interfaces2.begin(), interfaces2.end(),
+                          assetTagInterface) != interfaces2.end())
+            {
+                sdbusplus::asio::getProperty<std::string>(
+                    *crow::connections::systemBus, connectionName, path,
+                    assetTagInterface, "AssetTag",
+                    [asyncResp, chassisId(std::string(chassisId))](
+                        const boost::system::error_code ec2,
+                        const std::string& property) {
+                    if (ec2)
+                    {
+                        BMCWEB_LOG_DEBUG << "DBus response error for AssetTag";
+                        messages::internalError(asyncResp->res);
+                        return;
+                    }
+                    asyncResp->res.jsonValue["AssetTag"] = property;
+                    });
+            }
+
+            for (const char* interface : hasIndicatorLed)
+            {
+                if (std::find(interfaces2.begin(), interfaces2.end(),
+                              interface) != interfaces2.end())
+                {
+                    getIndicatorLedState(asyncResp);
+                    getLocationIndicatorActive(asyncResp);
+                    break;
+                }
+            }
+
+            crow::connections::systemBus->async_method_call(
+                [asyncResp, chassisId(std::string(chassisId))](
+                    const boost::system::error_code /*ec2*/,
+                    const dbus::utility::DBusPropertiesMap& propertiesList) {
+                for (const std::pair<std::string,
+                                     dbus::utility::DbusVariantType>& property :
+                     propertiesList)
+                {
+                    // Store DBus properties that are also
+                    // Redfish properties with same name and a
+                    // string value
+                    const std::string& propertyName = property.first;
+                    if ((propertyName == "PartNumber") ||
+                        (propertyName == "SerialNumber") ||
+                        (propertyName == "Manufacturer") ||
+                        (propertyName == "Model") ||
+                        (propertyName == "SparePartNumber"))
+                    {
+                        const std::string* value =
+                            std::get_if<std::string>(&property.second);
+                        if (value == nullptr)
+                        {
+                            BMCWEB_LOG_ERROR << "Null value returned for "
+                                             << propertyName;
+                            messages::internalError(asyncResp->res);
+                            return;
+                        }
+                        // SparePartNumber is optional on D-Bus
+                        // so skip if it is empty
+                        if (propertyName == "SparePartNumber")
+                        {
+                            if (value->empty())
+                            {
+                                continue;
+                            }
+                        }
+                        asyncResp->res.jsonValue[propertyName] = *value;
+                    }
+                }
+                asyncResp->res.jsonValue["Name"] = chassisId;
+                asyncResp->res.jsonValue["Id"] = chassisId;
+#ifdef BMCWEB_ALLOW_DEPRECATED_POWER_THERMAL
+                asyncResp->res.jsonValue["Thermal"]["@odata.id"] =
+                    "/redfish/v1/Chassis/" + chassisId + "/Thermal";
+                // Power object
+                asyncResp->res.jsonValue["Power"]["@odata.id"] =
+                    "/redfish/v1/Chassis/" + chassisId + "/Power";
+#endif
+                // SensorCollection
+                asyncResp->res.jsonValue["Sensors"]["@odata.id"] =
+                    "/redfish/v1/Chassis/" + chassisId + "/Sensors";
+                asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
+
+                nlohmann::json::array_t computerSystems;
+                nlohmann::json::object_t system;
+                system["@odata.id"] = "/redfish/v1/Systems/system";
+                computerSystems.push_back(std::move(system));
+                asyncResp->res.jsonValue["Links"]["ComputerSystems"] =
+                    std::move(computerSystems);
+
+                nlohmann::json::array_t managedBy;
+                nlohmann::json::object_t manager;
+                manager["@odata.id"] = "/redfish/v1/Managers/bmc";
+                managedBy.push_back(std::move(manager));
+                asyncResp->res.jsonValue["Links"]["ManagedBy"] =
+                    std::move(managedBy);
+                getChassisState(asyncResp);
+                },
+                connectionName, path, "org.freedesktop.DBus.Properties",
+                "GetAll", "xyz.openbmc_project.Inventory.Decorator.Asset");
+
+            for (const auto& interface : interfaces2)
+            {
+                if (interface == "xyz.openbmc_project.Common.UUID")
+                {
+                    getChassisUUID(asyncResp, connectionName, path);
+                }
+                else if (interface ==
+                         "xyz.openbmc_project.Inventory.Decorator.LocationCode")
+                {
+                    getChassisLocationCode(asyncResp, connectionName, path);
+                }
+            }
+
+            return;
+        }
+
+        // Couldn't find an object with that name.  return an error
+        messages::resourceNotFound(asyncResp->res, "#Chassis.v1_16_0.Chassis",
+                                   chassisId);
+        },
+        "xyz.openbmc_project.ObjectMapper",
+        "/xyz/openbmc_project/object_mapper",
+        "xyz.openbmc_project.ObjectMapper", "GetSubTree",
+        "/xyz/openbmc_project/inventory", 0, interfaces);
+
+    getPhysicalSecurityData(asyncResp);
+}
+
+inline void
+    handleChassisPatch(App& app, const crow::Request& req,
+                       const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+                       const std::string& param)
+{
+    if (!redfish::setUpRedfishRoute(app, req, asyncResp))
+    {
+        return;
+    }
+    std::optional<bool> locationIndicatorActive;
+    std::optional<std::string> indicatorLed;
+
+    if (param.empty())
+    {
+        return;
+    }
+
+    if (!json_util::readJsonPatch(
+            req, asyncResp->res, "LocationIndicatorActive",
+            locationIndicatorActive, "IndicatorLED", indicatorLed))
+    {
+        return;
+    }
+
+    // TODO (Gunnar): Remove IndicatorLED after enough time has passed
+    if (!locationIndicatorActive && !indicatorLed)
+    {
+        return; // delete this when we support more patch properties
+    }
+    if (indicatorLed)
+    {
+        asyncResp->res.addHeader(
+            boost::beast::http::field::warning,
+            "299 - \"IndicatorLED is deprecated. Use LocationIndicatorActive instead.\"");
+    }
+
+    const std::array<const char*, 2> interfaces = {
+        "xyz.openbmc_project.Inventory.Item.Board",
+        "xyz.openbmc_project.Inventory.Item.Chassis"};
+
+    const std::string& chassisId = param;
+
+    crow::connections::systemBus->async_method_call(
+        [asyncResp, chassisId, locationIndicatorActive,
+         indicatorLed](const boost::system::error_code ec,
+                       const dbus::utility::MapperGetSubTreeResponse& subtree) {
+        if (ec)
+        {
+            messages::internalError(asyncResp->res);
+            return;
+        }
+
+        // Iterate over all retrieved ObjectPaths.
+        for (const std::pair<
+                 std::string,
+                 std::vector<std::pair<std::string, std::vector<std::string>>>>&
+                 object : subtree)
+        {
+            const std::string& path = object.first;
+            const std::vector<std::pair<std::string, std::vector<std::string>>>&
+                connectionNames = object.second;
+
+            sdbusplus::message::object_path objPath(path);
+            if (objPath.filename() != chassisId)
+            {
+                continue;
+            }
+
+            if (connectionNames.empty())
+            {
+                BMCWEB_LOG_ERROR << "Got 0 Connection names";
+                continue;
+            }
+
+            const std::vector<std::string>& interfaces3 =
+                connectionNames[0].second;
+
+            const std::array<const char*, 2> hasIndicatorLed = {
+                "xyz.openbmc_project.Inventory.Item.Panel",
+                "xyz.openbmc_project.Inventory.Item.Board.Motherboard"};
+            bool indicatorChassis = false;
+            for (const char* interface : hasIndicatorLed)
+            {
+                if (std::find(interfaces3.begin(), interfaces3.end(),
+                              interface) != interfaces3.end())
+                {
+                    indicatorChassis = true;
+                    break;
+                }
+            }
+            if (locationIndicatorActive)
+            {
+                if (indicatorChassis)
+                {
+                    setLocationIndicatorActive(asyncResp,
+                                               *locationIndicatorActive);
+                }
+                else
+                {
+                    messages::propertyUnknown(asyncResp->res,
+                                              "LocationIndicatorActive");
+                }
+            }
+            if (indicatorLed)
+            {
+                if (indicatorChassis)
+                {
+                    setIndicatorLedState(asyncResp, *indicatorLed);
+                }
+                else
+                {
+                    messages::propertyUnknown(asyncResp->res, "IndicatorLED");
+                }
+            }
+            return;
+        }
+
+        messages::resourceNotFound(asyncResp->res, "#Chassis.v1_14_0.Chassis",
+                                   chassisId);
+        },
+        "xyz.openbmc_project.ObjectMapper",
+        "/xyz/openbmc_project/object_mapper",
+        "xyz.openbmc_project.ObjectMapper", "GetSubTree",
+        "/xyz/openbmc_project/inventory", 0, interfaces);
+}
+
 /**
  * Chassis override class for delivering Chassis Schema
  * Functions triggers appropriate requests on DBus
@@ -208,370 +570,12 @@
     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/")
         .privileges(redfish::privileges::getChassis)
         .methods(boost::beast::http::verb::get)(
-            [&app](const crow::Request& req,
-                   const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-                   const std::string& chassisId) {
-        if (!redfish::setUpRedfishRoute(app, req, asyncResp))
-        {
-            return;
-        }
-        const std::array<const char*, 2> interfaces = {
-            "xyz.openbmc_project.Inventory.Item.Board",
-            "xyz.openbmc_project.Inventory.Item.Chassis"};
-
-        crow::connections::systemBus->async_method_call(
-            [asyncResp, chassisId(std::string(chassisId))](
-                const boost::system::error_code ec,
-                const dbus::utility::MapperGetSubTreeResponse& subtree) {
-            if (ec)
-            {
-                messages::internalError(asyncResp->res);
-                return;
-            }
-            // Iterate over all retrieved ObjectPaths.
-            for (const std::pair<std::string,
-                                 std::vector<std::pair<
-                                     std::string, std::vector<std::string>>>>&
-                     object : subtree)
-            {
-                const std::string& path = object.first;
-                const std::vector<
-                    std::pair<std::string, std::vector<std::string>>>&
-                    connectionNames = object.second;
-
-                sdbusplus::message::object_path objPath(path);
-                if (objPath.filename() != chassisId)
-                {
-                    continue;
-                }
-
-                auto health = std::make_shared<HealthPopulate>(asyncResp);
-
-                sdbusplus::asio::getProperty<std::vector<std::string>>(
-                    *crow::connections::systemBus,
-                    "xyz.openbmc_project.ObjectMapper", path + "/all_sensors",
-                    "xyz.openbmc_project.Association", "endpoints",
-                    [health](const boost::system::error_code ec2,
-                             const std::vector<std::string>& resp) {
-                    if (ec2)
-                    {
-                        return; // no sensors = no failures
-                    }
-                    health->inventory = resp;
-                    });
-
-                health->populate();
-
-                if (connectionNames.empty())
-                {
-                    BMCWEB_LOG_ERROR << "Got 0 Connection names";
-                    continue;
-                }
-
-                asyncResp->res.jsonValue["@odata.type"] =
-                    "#Chassis.v1_16_0.Chassis";
-                asyncResp->res.jsonValue["@odata.id"] =
-                    "/redfish/v1/Chassis/" + chassisId;
-                asyncResp->res.jsonValue["Name"] = "Chassis Collection";
-                asyncResp->res.jsonValue["ChassisType"] = "RackMount";
-                asyncResp->res
-                    .jsonValue["Actions"]["#Chassis.Reset"]["target"] =
-                    "/redfish/v1/Chassis/" + chassisId +
-                    "/Actions/Chassis.Reset";
-                asyncResp->res.jsonValue["Actions"]["#Chassis.Reset"]
-                                        ["@Redfish.ActionInfo"] =
-                    "/redfish/v1/Chassis/" + chassisId + "/ResetActionInfo";
-                asyncResp->res.jsonValue["PCIeDevices"]["@odata.id"] =
-                    "/redfish/v1/Systems/system/PCIeDevices";
-
-                sdbusplus::asio::getProperty<std::vector<std::string>>(
-                    *crow::connections::systemBus,
-                    "xyz.openbmc_project.ObjectMapper", path + "/drive",
-                    "xyz.openbmc_project.Association", "endpoints",
-                    [asyncResp,
-                     chassisId](const boost::system::error_code ec3,
-                                const std::vector<std::string>& resp) {
-                    if (ec3 || resp.empty())
-                    {
-                        return; // no drives = no failures
-                    }
-
-                    nlohmann::json reference;
-                    reference["odata.id"] = crow::utility::urlFromPieces(
-                        "redfish", "v1", "Chassis", chassisId, "Drives");
-                    asyncResp->res.jsonValue["Drives"] = std::move(reference);
-                    });
-
-                const std::string& connectionName = connectionNames[0].first;
-
-                const std::vector<std::string>& interfaces2 =
-                    connectionNames[0].second;
-                const std::array<const char*, 2> hasIndicatorLed = {
-                    "xyz.openbmc_project.Inventory.Item.Panel",
-                    "xyz.openbmc_project.Inventory.Item.Board.Motherboard"};
-
-                const std::string assetTagInterface =
-                    "xyz.openbmc_project.Inventory.Decorator.AssetTag";
-                if (std::find(interfaces2.begin(), interfaces2.end(),
-                              assetTagInterface) != interfaces2.end())
-                {
-                    sdbusplus::asio::getProperty<std::string>(
-                        *crow::connections::systemBus, connectionName, path,
-                        assetTagInterface, "AssetTag",
-                        [asyncResp, chassisId(std::string(chassisId))](
-                            const boost::system::error_code ec2,
-                            const std::string& property) {
-                        if (ec2)
-                        {
-                            BMCWEB_LOG_DEBUG
-                                << "DBus response error for AssetTag";
-                            messages::internalError(asyncResp->res);
-                            return;
-                        }
-                        asyncResp->res.jsonValue["AssetTag"] = property;
-                        });
-                }
-
-                for (const char* interface : hasIndicatorLed)
-                {
-                    if (std::find(interfaces2.begin(), interfaces2.end(),
-                                  interface) != interfaces2.end())
-                    {
-                        getIndicatorLedState(asyncResp);
-                        getLocationIndicatorActive(asyncResp);
-                        break;
-                    }
-                }
-
-                crow::connections::systemBus->async_method_call(
-                    [asyncResp, chassisId(std::string(chassisId))](
-                        const boost::system::error_code /*ec2*/,
-                        const dbus::utility::DBusPropertiesMap&
-                            propertiesList) {
-                    for (const std::pair<std::string,
-                                         dbus::utility::DbusVariantType>&
-                             property : propertiesList)
-                    {
-                        // Store DBus properties that are also
-                        // Redfish properties with same name and a
-                        // string value
-                        const std::string& propertyName = property.first;
-                        if ((propertyName == "PartNumber") ||
-                            (propertyName == "SerialNumber") ||
-                            (propertyName == "Manufacturer") ||
-                            (propertyName == "Model") ||
-                            (propertyName == "SparePartNumber"))
-                        {
-                            const std::string* value =
-                                std::get_if<std::string>(&property.second);
-                            if (value == nullptr)
-                            {
-                                BMCWEB_LOG_ERROR << "Null value returned for "
-                                                 << propertyName;
-                                messages::internalError(asyncResp->res);
-                                return;
-                            }
-                            // SparePartNumber is optional on D-Bus
-                            // so skip if it is empty
-                            if (propertyName == "SparePartNumber")
-                            {
-                                if (value->empty())
-                                {
-                                    continue;
-                                }
-                            }
-                            asyncResp->res.jsonValue[propertyName] = *value;
-                        }
-                    }
-                    asyncResp->res.jsonValue["Name"] = chassisId;
-                    asyncResp->res.jsonValue["Id"] = chassisId;
-#ifdef BMCWEB_ALLOW_DEPRECATED_POWER_THERMAL
-                    asyncResp->res.jsonValue["Thermal"]["@odata.id"] =
-                        "/redfish/v1/Chassis/" + chassisId + "/Thermal";
-                    // Power object
-                    asyncResp->res.jsonValue["Power"]["@odata.id"] =
-                        "/redfish/v1/Chassis/" + chassisId + "/Power";
-#endif
-                    // SensorCollection
-                    asyncResp->res.jsonValue["Sensors"]["@odata.id"] =
-                        "/redfish/v1/Chassis/" + chassisId + "/Sensors";
-                    asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
-
-                    nlohmann::json::array_t computerSystems;
-                    nlohmann::json::object_t system;
-                    system["@odata.id"] = "/redfish/v1/Systems/system";
-                    computerSystems.push_back(std::move(system));
-                    asyncResp->res.jsonValue["Links"]["ComputerSystems"] =
-                        std::move(computerSystems);
-
-                    nlohmann::json::array_t managedBy;
-                    nlohmann::json::object_t manager;
-                    manager["@odata.id"] = "/redfish/v1/Managers/bmc";
-                    managedBy.push_back(std::move(manager));
-                    asyncResp->res.jsonValue["Links"]["ManagedBy"] =
-                        std::move(managedBy);
-                    getChassisState(asyncResp);
-                    },
-                    connectionName, path, "org.freedesktop.DBus.Properties",
-                    "GetAll", "xyz.openbmc_project.Inventory.Decorator.Asset");
-
-                for (const auto& interface : interfaces2)
-                {
-                    if (interface == "xyz.openbmc_project.Common.UUID")
-                    {
-                        getChassisUUID(asyncResp, connectionName, path);
-                    }
-                    else if (
-                        interface ==
-                        "xyz.openbmc_project.Inventory.Decorator.LocationCode")
-                    {
-                        getChassisLocationCode(asyncResp, connectionName, path);
-                    }
-                }
-
-                return;
-            }
-
-            // Couldn't find an object with that name.  return an error
-            messages::resourceNotFound(asyncResp->res,
-                                       "#Chassis.v1_16_0.Chassis", chassisId);
-            },
-            "xyz.openbmc_project.ObjectMapper",
-            "/xyz/openbmc_project/object_mapper",
-            "xyz.openbmc_project.ObjectMapper", "GetSubTree",
-            "/xyz/openbmc_project/inventory", 0, interfaces);
-
-        getPhysicalSecurityData(asyncResp);
-        });
+            std::bind_front(handleChassisGet, std::ref(app)));
 
     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/")
         .privileges(redfish::privileges::patchChassis)
         .methods(boost::beast::http::verb::patch)(
-            [&app](const crow::Request& req,
-                   const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-                   const std::string& param) {
-        if (!redfish::setUpRedfishRoute(app, req, asyncResp))
-        {
-            return;
-        }
-        std::optional<bool> locationIndicatorActive;
-        std::optional<std::string> indicatorLed;
-
-        if (param.empty())
-        {
-            return;
-        }
-
-        if (!json_util::readJsonPatch(
-                req, asyncResp->res, "LocationIndicatorActive",
-                locationIndicatorActive, "IndicatorLED", indicatorLed))
-        {
-            return;
-        }
-
-        // TODO (Gunnar): Remove IndicatorLED after enough time has passed
-        if (!locationIndicatorActive && !indicatorLed)
-        {
-            return; // delete this when we support more patch properties
-        }
-        if (indicatorLed)
-        {
-            asyncResp->res.addHeader(
-                boost::beast::http::field::warning,
-                "299 - \"IndicatorLED is deprecated. Use LocationIndicatorActive instead.\"");
-        }
-
-        const std::array<const char*, 2> interfaces = {
-            "xyz.openbmc_project.Inventory.Item.Board",
-            "xyz.openbmc_project.Inventory.Item.Chassis"};
-
-        const std::string& chassisId = param;
-
-        crow::connections::systemBus->async_method_call(
-            [asyncResp, chassisId, locationIndicatorActive, indicatorLed](
-                const boost::system::error_code ec,
-                const dbus::utility::MapperGetSubTreeResponse& subtree) {
-            if (ec)
-            {
-                messages::internalError(asyncResp->res);
-                return;
-            }
-
-            // Iterate over all retrieved ObjectPaths.
-            for (const std::pair<std::string,
-                                 std::vector<std::pair<
-                                     std::string, std::vector<std::string>>>>&
-                     object : subtree)
-            {
-                const std::string& path = object.first;
-                const std::vector<
-                    std::pair<std::string, std::vector<std::string>>>&
-                    connectionNames = object.second;
-
-                sdbusplus::message::object_path objPath(path);
-                if (objPath.filename() != chassisId)
-                {
-                    continue;
-                }
-
-                if (connectionNames.empty())
-                {
-                    BMCWEB_LOG_ERROR << "Got 0 Connection names";
-                    continue;
-                }
-
-                const std::vector<std::string>& interfaces3 =
-                    connectionNames[0].second;
-
-                const std::array<const char*, 2> hasIndicatorLed = {
-                    "xyz.openbmc_project.Inventory.Item.Panel",
-                    "xyz.openbmc_project.Inventory.Item.Board.Motherboard"};
-                bool indicatorChassis = false;
-                for (const char* interface : hasIndicatorLed)
-                {
-                    if (std::find(interfaces3.begin(), interfaces3.end(),
-                                  interface) != interfaces3.end())
-                    {
-                        indicatorChassis = true;
-                        break;
-                    }
-                }
-                if (locationIndicatorActive)
-                {
-                    if (indicatorChassis)
-                    {
-                        setLocationIndicatorActive(asyncResp,
-                                                   *locationIndicatorActive);
-                    }
-                    else
-                    {
-                        messages::propertyUnknown(asyncResp->res,
-                                                  "LocationIndicatorActive");
-                    }
-                }
-                if (indicatorLed)
-                {
-                    if (indicatorChassis)
-                    {
-                        setIndicatorLedState(asyncResp, *indicatorLed);
-                    }
-                    else
-                    {
-                        messages::propertyUnknown(asyncResp->res,
-                                                  "IndicatorLED");
-                    }
-                }
-                return;
-            }
-
-            messages::resourceNotFound(asyncResp->res,
-                                       "#Chassis.v1_14_0.Chassis", chassisId);
-            },
-            "xyz.openbmc_project.ObjectMapper",
-            "/xyz/openbmc_project/object_mapper",
-            "xyz.openbmc_project.ObjectMapper", "GetSubTree",
-            "/xyz/openbmc_project/inventory", 0, interfaces);
-        });
+            std::bind_front(handleChassisPatch, std::ref(app)));
 }
 
 inline void
@@ -633,6 +637,36 @@
         busName, path, interface, method, "/", 0, interfaces);
 }
 
+inline void handleChassisResetActionInfoPost(
+    App& app, const crow::Request& req,
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    const std::string& /*chassisId*/)
+{
+    if (!redfish::setUpRedfishRoute(app, req, asyncResp))
+    {
+        return;
+    }
+    BMCWEB_LOG_DEBUG << "Post Chassis Reset.";
+
+    std::string resetType;
+
+    if (!json_util::readJsonAction(req, asyncResp->res, "ResetType", resetType))
+    {
+        return;
+    }
+
+    if (resetType != "PowerCycle")
+    {
+        BMCWEB_LOG_DEBUG << "Invalid property value for ResetType: "
+                         << resetType;
+        messages::actionParameterNotSupported(asyncResp->res, resetType,
+                                              "ResetType");
+
+        return;
+    }
+    doChassisPowerCycle(asyncResp);
+}
+
 /**
  * ChassisResetAction class supports the POST method for the Reset
  * action.
@@ -645,34 +679,35 @@
     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Actions/Chassis.Reset/")
         .privileges(redfish::privileges::postChassis)
         .methods(boost::beast::http::verb::post)(
-            [&app](const crow::Request& req,
-                   const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-                   const std::string&) {
-        if (!redfish::setUpRedfishRoute(app, req, asyncResp))
-        {
-            return;
-        }
-        BMCWEB_LOG_DEBUG << "Post Chassis Reset.";
+            std::bind_front(handleChassisResetActionInfoPost, std::ref(app)));
+}
 
-        std::string resetType;
+inline void handleChassisResetActionInfoGet(
+    App& app, const crow::Request& req,
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    const std::string& chassisId)
+{
+    if (!redfish::setUpRedfishRoute(app, req, asyncResp))
+    {
+        return;
+    }
+    asyncResp->res.jsonValue["@odata.type"] = "#ActionInfo.v1_1_2.ActionInfo";
+    asyncResp->res.jsonValue["@odata.id"] =
+        "/redfish/v1/Chassis/" + chassisId + "/ResetActionInfo";
+    asyncResp->res.jsonValue["Name"] = "Reset Action Info";
 
-        if (!json_util::readJsonAction(req, asyncResp->res, "ResetType",
-                                       resetType))
-        {
-            return;
-        }
+    asyncResp->res.jsonValue["Id"] = "ResetActionInfo";
+    nlohmann::json::array_t parameters;
+    nlohmann::json::object_t parameter;
+    parameter["Name"] = "ResetType";
+    parameter["Required"] = true;
+    parameter["DataType"] = "String";
+    nlohmann::json::array_t allowed;
+    allowed.push_back("PowerCycle");
+    parameter["AllowableValues"] = std::move(allowed);
+    parameters.push_back(std::move(parameter));
 
-        if (resetType != "PowerCycle")
-        {
-            BMCWEB_LOG_DEBUG << "Invalid property value for ResetType: "
-                             << resetType;
-            messages::actionParameterNotSupported(asyncResp->res, resetType,
-                                                  "ResetType");
-
-            return;
-        }
-        doChassisPowerCycle(asyncResp);
-        });
+    asyncResp->res.jsonValue["Parameters"] = std::move(parameters);
 }
 
 /**
@@ -684,32 +719,7 @@
     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ResetActionInfo/")
         .privileges(redfish::privileges::getActionInfo)
         .methods(boost::beast::http::verb::get)(
-            [&app](const crow::Request& req,
-                   const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-                   const std::string& chassisId) {
-        if (!redfish::setUpRedfishRoute(app, req, asyncResp))
-        {
-            return;
-        }
-        asyncResp->res.jsonValue["@odata.type"] =
-            "#ActionInfo.v1_1_2.ActionInfo";
-        asyncResp->res.jsonValue["@odata.id"] =
-            "/redfish/v1/Chassis/" + chassisId + "/ResetActionInfo";
-        asyncResp->res.jsonValue["Name"] = "Reset Action Info";
-
-        asyncResp->res.jsonValue["Id"] = "ResetActionInfo";
-        nlohmann::json::array_t parameters;
-        nlohmann::json::object_t parameter;
-        parameter["Name"] = "ResetType";
-        parameter["Required"] = true;
-        parameter["DataType"] = "String";
-        nlohmann::json::array_t allowed;
-        allowed.push_back("PowerCycle");
-        parameter["AllowableValues"] = std::move(allowed);
-        parameters.push_back(std::move(parameter));
-
-        asyncResp->res.jsonValue["Parameters"] = std::move(parameters);
-        });
+            std::bind_front(handleChassisResetActionInfoGet, std::ref(app)));
 }
 
 } // namespace redfish
diff --git a/redfish-core/lib/chassis_test.cpp b/redfish-core/lib/chassis_test.cpp
new file mode 100644
index 0000000..a043859
--- /dev/null
+++ b/redfish-core/lib/chassis_test.cpp
@@ -0,0 +1,60 @@
+#include "app.hpp"
+#include "async_resp.hpp"
+#include "chassis.hpp"
+#include "http_request.hpp"
+#include "http_response.hpp"
+
+#include <boost/beast/core/string_type.hpp>
+#include <boost/beast/http/message.hpp>
+#include <nlohmann/json.hpp>
+
+#include <system_error>
+
+#include <gtest/gtest.h>
+
+namespace redfish
+{
+namespace
+{
+
+void assertChassisResetActionInfoGet(const std::string& chassisId,
+                                     crow::Response& res)
+{
+    EXPECT_EQ(res.jsonValue["@odata.type"], "#ActionInfo.v1_1_2.ActionInfo");
+    EXPECT_EQ(res.jsonValue["@odata.id"],
+              "/redfish/v1/Chassis/" + chassisId + "/ResetActionInfo");
+    EXPECT_EQ(res.jsonValue["Name"], "Reset Action Info");
+
+    EXPECT_EQ(res.jsonValue["Id"], "ResetActionInfo");
+
+    nlohmann::json::array_t parameters;
+    nlohmann::json::object_t parameter;
+    parameter["Name"] = "ResetType";
+    parameter["Required"] = true;
+    parameter["DataType"] = "String";
+    nlohmann::json::array_t allowed;
+    allowed.push_back("PowerCycle");
+    parameter["AllowableValues"] = std::move(allowed);
+    parameters.push_back(std::move(parameter));
+
+    EXPECT_EQ(res.jsonValue["Parameters"], parameters);
+}
+
+TEST(HandleChassisResetActionInfoGet, StaticAttributesAreExpected)
+{
+
+    auto response = std::make_shared<bmcweb::AsyncResp>();
+    std::error_code err;
+    crow::Request request{{boost::beast::http::verb::get, "/whatever", 11},
+                          err};
+
+    std::string fakeChassis = "fakeChassis";
+    response->res.setCompleteRequestHandler(
+        std::bind_front(assertChassisResetActionInfoGet, fakeChassis));
+
+    crow::App app;
+    handleChassisResetActionInfoGet(app, request, response, fakeChassis);
+}
+
+} // namespace
+} // namespace redfish
\ No newline at end of file
diff --git a/redfish-core/lib/redfish_util.hpp b/redfish-core/lib/redfish_util.hpp
index e050594..04d6ceb 100644
--- a/redfish-core/lib/redfish_util.hpp
+++ b/redfish-core/lib/redfish_util.hpp
@@ -17,6 +17,10 @@
 #ifndef BMCWEB_ENABLE_REDFISH_ONE_CHASSIS
 
 #include <dbus_utility.hpp>
+#include <sdbusplus/asio/property.hpp>
+
+#include <charconv>
+
 namespace redfish
 {