Implements PowerSupplies schema

This commit implements the Redfish PowerSupplyCollection at
/redfish/v1/Chassis/<chassis Id>/PowerSubsystem/PowerSupplies.
It shall contain an array of links to resources of type PowerSupply
that represent the power supplies that provide power to this chassis.
For the association between power supply and chassis, refer to[1].

Also, the members property is implemented in the next commit with the
PowerSupply implementation, this is so the validator will pass.

[1] https://gerrit.openbmc.org/c/openbmc/phosphor-dbus-interfaces/+/57428

Tested: Validator passes
1. curl -k  -H "X-Auth-Token: $token" -X GET
https://${bmc}/redfish/v1/Chassis/chassis/PowerSubsystem/
PowerSupplies
{
"@odata.id": "/redfish/v1/Chassis/chassis/PowerSubsystem/
              PowerSupplies"
"@odata.type": "#PowerSupplyCollection.PowerSupplyCollection",
"Description": "The collection of PowerSupply resource instances
                chassis",
"Members": [
],
"Members@odata.count": 0,
"Name": "Power Supply Collection"
}

2. Bad chassisId
curl -k  -H "X-Auth-Token: $token" -X GET
https://${bmc}/redfish/v1/Chassis/chassisError/PowerSubsystem/
PowerSupplies
{
"error": {
"@Message.ExtendedInfo": [
{
"@odata.type": "#Message.v1_1_1.Message",
"Message": "The requested resource of type Chassis named
            'chassisError' was not found.",
"MessageArgs": [
"Chassis",
"chassisError"
],
"MessageId": "Base.1.13.0.ResourceNotFound",
"MessageSeverity": "Critical",
"Resolution": "Provide a valid resource identifier and resubmit the
               request."
}
],
"code": "Base.1.13.0.ResourceNotFound",
"message": "The requested resource of type Chassis named
            'chassisError' was not found."
}
}

Signed-off-by: George Liu <liuxiwei@inspur.com>
Change-Id: I48e087d6fb52013e3a96b44391cc4d86049ac176
diff --git a/redfish-core/lib/power_supply.hpp b/redfish-core/lib/power_supply.hpp
new file mode 100644
index 0000000..9fffa19
--- /dev/null
+++ b/redfish-core/lib/power_supply.hpp
@@ -0,0 +1,123 @@
+#pragma once
+
+#include "app.hpp"
+#include "dbus_utility.hpp"
+#include "query.hpp"
+#include "registries/privilege_registry.hpp"
+#include "utils/chassis_utils.hpp"
+
+#include <memory>
+#include <optional>
+#include <string>
+
+namespace redfish
+{
+
+inline void updatePowerSupplyList(
+    const std::shared_ptr<bmcweb::AsyncResp>& /* asyncResp */,
+    const std::string& /* chassisId */,
+    const std::string& /* powerSupplyPath */)
+{
+    // TODO In order for the validator to pass, the Members property will be
+    // implemented on the next commit
+}
+
+inline void
+    doPowerSupplyCollection(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+                            const std::string& chassisId,
+                            const std::optional<std::string>& validChassisPath)
+{
+    if (!validChassisPath)
+    {
+        messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
+        return;
+    }
+
+    asyncResp->res.addHeader(
+        boost::beast::http::field::link,
+        "</redfish/v1/JsonSchemas/PowerSupplyCollection/PowerSupplyCollection.json>; rel=describedby");
+    asyncResp->res.jsonValue["@odata.type"] =
+        "#PowerSupplyCollection.PowerSupplyCollection";
+    asyncResp->res.jsonValue["Name"] = "Power Supply Collection";
+    asyncResp->res.jsonValue["@odata.id"] =
+        crow::utility::urlFromPieces("redfish", "v1", "Chassis", chassisId,
+                                     "PowerSubsystem", "PowerSupplies");
+    asyncResp->res.jsonValue["Description"] =
+        "The collection of PowerSupply resource instances.";
+
+    std::string powerPath = *validChassisPath + "/powered_by";
+    dbus::utility::getAssociationEndPoints(
+        powerPath, [asyncResp, chassisId](
+                       const boost::system::error_code& ec,
+                       const dbus::utility::MapperEndPoints& endpoints) {
+            if (ec)
+            {
+                if (ec.value() != EBADR)
+                {
+                    BMCWEB_LOG_ERROR << "DBUS response error" << ec.value();
+                    messages::internalError(asyncResp->res);
+                }
+                return;
+            }
+
+            for (const auto& endpoint : endpoints)
+            {
+                updatePowerSupplyList(asyncResp, chassisId, endpoint);
+            }
+        });
+}
+
+inline void handlePowerSupplyCollectionHead(
+    App& app, const crow::Request& req,
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    const std::string& chassisId)
+{
+    if (!redfish::setUpRedfishRoute(app, req, asyncResp))
+    {
+        return;
+    }
+
+    redfish::chassis_utils::getValidChassisPath(
+        asyncResp, chassisId,
+        [asyncResp,
+         chassisId](const std::optional<std::string>& validChassisPath) {
+        if (!validChassisPath)
+        {
+            messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
+            return;
+        }
+        asyncResp->res.addHeader(
+            boost::beast::http::field::link,
+            "</redfish/v1/JsonSchemas/PowerSupplyCollection/PowerSupplyCollection.json>; rel=describedby");
+        });
+}
+
+inline void handlePowerSupplyCollectionGet(
+    App& app, const crow::Request& req,
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    const std::string& chassisId)
+{
+    if (!redfish::setUpRedfishRoute(app, req, asyncResp))
+    {
+        return;
+    }
+
+    redfish::chassis_utils::getValidChassisPath(
+        asyncResp, chassisId,
+        std::bind_front(doPowerSupplyCollection, asyncResp, chassisId));
+}
+
+inline void requestRoutesPowerSupplyCollection(App& app)
+{
+    BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/PowerSubsystem/PowerSupplies/")
+        .privileges(redfish::privileges::headPowerSupplyCollection)
+        .methods(boost::beast::http::verb::head)(
+            std::bind_front(handlePowerSupplyCollectionHead, std::ref(app)));
+
+    BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/PowerSubsystem/PowerSupplies/")
+        .privileges(redfish::privileges::getPowerSupplyCollection)
+        .methods(boost::beast::http::verb::get)(
+            std::bind_front(handlePowerSupplyCollectionGet, std::ref(app)));
+}
+
+} // namespace redfish