Add link support to SystemsCollection

Similar to the other patches in the series, add a link header to all
ComputerSystem resources, along with an explicit HEAD operator.

Tested:
All of the below return a link header
GET /redfish/v1/Systems
HEAD /redfish/v1/Systems
GET /redfish/v1/Systems/system
HEAD /redfish/v1/Systems/system
GET /redfish/v1/Systems/system/ResetActionInfo
HEAD /redfish/v1/Systems/system/ResetActionInfo

Signed-off-by: Ed Tanous <edtanous@google.com>
Change-Id: Ie2fc6b16be70604613ba5322e1a358974e4e6cde
diff --git a/redfish-core/lib/systems.hpp b/redfish-core/lib/systems.hpp
index fa2c8e7..230598b 100644
--- a/redfish-core/lib/systems.hpp
+++ b/redfish-core/lib/systems.hpp
@@ -2621,6 +2621,19 @@
     BMCWEB_LOG_DEBUG << "EXIT: Set idle power saver parameters";
 }
 
+inline void handleComputerSystemHead(
+    crow::App& app, const crow::Request& req,
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
+{
+    if (!redfish::setUpRedfishRoute(app, req, asyncResp))
+    {
+        return;
+    }
+    asyncResp->res.addHeader(
+        boost::beast::http::field::link,
+        "</redfish/v1/JsonSchemas/ComputerSystemCollection/ComputerSystemCollection.json>; rel=describedby");
+}
+
 /**
  * SystemsCollection derived class for delivering ComputerSystems Collection
  * Schema
@@ -2628,6 +2641,11 @@
 inline void requestRoutesSystemsCollection(App& app)
 {
     BMCWEB_ROUTE(app, "/redfish/v1/Systems/")
+        .privileges(redfish::privileges::headComputerSystemCollection)
+        .methods(boost::beast::http::verb::head)(
+            std::bind_front(handleComputerSystemHead, std::ref(app)));
+
+    BMCWEB_ROUTE(app, "/redfish/v1/Systems/")
         .privileges(redfish::privileges::getComputerSystemCollection)
         .methods(boost::beast::http::verb::get)(
             [&app](const crow::Request& req,
@@ -2636,6 +2654,10 @@
         {
             return;
         }
+
+        asyncResp->res.addHeader(
+            boost::beast::http::field::link,
+            "</redfish/v1/JsonSchemas/ComputerSystemCollection.json>; rel=describedby");
         asyncResp->res.jsonValue["@odata.type"] =
             "#ComputerSystemCollection.ComputerSystemCollection";
         asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Systems";
@@ -2820,12 +2842,30 @@
         });
 }
 
+void handleComputerSystemCollectionHead(
+    App& app, const crow::Request& req,
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
+{
+    if (!redfish::setUpRedfishRoute(app, req, asyncResp))
+    {
+        return;
+    }
+
+    asyncResp->res.addHeader(
+        boost::beast::http::field::link,
+        "</redfish/v1/JsonSchemas/ComputerSystem/ComputerSystem.json>; rel=describedby");
+}
+
 /**
  * Systems derived class for delivering Computer Systems Schema.
  */
 inline void requestRoutesSystems(App& app)
 {
 
+    BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/")
+        .privileges(redfish::privileges::headComputerSystem)
+        .methods(boost::beast::http::verb::head)(
+            std::bind_front(handleComputerSystemCollectionHead, std::ref(app)));
     /**
      * Functions triggers appropriate requests on DBus
      */
@@ -2838,6 +2878,9 @@
         {
             return;
         }
+        asyncResp->res.addHeader(
+            boost::beast::http::field::link,
+            "</redfish/v1/JsonSchemas/ComputerSystem/ComputerSystem.json>; rel=describedby");
         asyncResp->res.jsonValue["@odata.type"] =
             "#ComputerSystem.v1_16_0.ComputerSystem";
         asyncResp->res.jsonValue["Name"] = "system";
@@ -2963,6 +3006,10 @@
         {
             return;
         }
+        asyncResp->res.addHeader(
+            boost::beast::http::field::link,
+            "</redfish/v1/JsonSchemas/ComputerSystem/ComputerSystem.json>; rel=describedby");
+
         std::optional<bool> locationIndicatorActive;
         std::optional<std::string> indicatorLed;
         std::optional<std::string> assetTag;
@@ -3067,12 +3114,29 @@
         });
 }
 
+void handleSystemCollectionResetActionHead(
+    crow::App& app, const crow::Request& req,
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
+{
+    if (!redfish::setUpRedfishRoute(app, req, asyncResp))
+    {
+        return;
+    }
+    asyncResp->res.addHeader(
+        boost::beast::http::field::link,
+        "</redfish/v1/JsonSchemas/ActionInfo/ActionInfo.json>; rel=describedby");
+}
+
 /**
  * SystemResetActionInfo derived class for delivering Computer Systems
  * ResetType AllowableValues using ResetInfo schema.
  */
 inline void requestRoutesSystemResetActionInfo(App& app)
 {
+    BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/ResetActionInfo/")
+        .privileges(redfish::privileges::headActionInfo)
+        .methods(boost::beast::http::verb::head)(std::bind_front(
+            handleSystemCollectionResetActionHead, std::ref(app)));
     /**
      * Functions triggers appropriate requests on DBus
      */
@@ -3085,6 +3149,9 @@
         {
             return;
         }
+        asyncResp->res.addHeader(
+            boost::beast::http::field::link,
+            "</redfish/v1/JsonSchemas/ActionInfo/ActionInfo.json>; rel=describedby");
 
         asyncResp->res.jsonValue["@odata.id"] =
             "/redfish/v1/Systems/system/ResetActionInfo";