AssetTag: Add PATCH support

Use the same AssetTag logic from the GET.
Look for xyz.openbmc_project.Inventory.Item.System interface then
call that service and path to set the AssetTag.

This assumes there is 1 "Item.System".
Considered something like
"if (!boost::ends_with(path, "system"))
continue;
"
but no where else does bmcweb check that the Item.System is named
"system" i.e. that /redfish/v1/Systems/system is actually named
"system" on D-Bus.

Considered looking that the service had the interface if not move
to the next service but the GET code does not so just followed it.

Tested:
 curl -k-X PATCH -d  '{"AssetTag": "Paramo"}'  https://${bmc}/redfish/v1/Systems/system/
 curl -k  https://${bmc}/redfish/v1/Systems/system/
{
  "@odata.id": "/redfish/v1/Systems/system",
  "@odata.type": "#ComputerSystem.v1_12_0.ComputerSystem",
  "Actions": {
    "#ComputerSystem.Reset": {
      "@Redfish.ActionInfo": "/redfish/v1/Systems/system/ResetActionInfo",
      "target": "/redfish/v1/Systems/system/Actions/ComputerSystem.Reset"
    }
  },
  "AssetTag": "Paramo",
 ...
Validator passes.

Change-Id: I45f80a8a69457f76e6e83ad2333856abe61de933
Signed-off-by: Gunnar Mills <gmills@us.ibm.com>
diff --git a/redfish-core/lib/systems.hpp b/redfish-core/lib/systems.hpp
index d042ca3..5f9801b 100644
--- a/redfish-core/lib/systems.hpp
+++ b/redfish-core/lib/systems.hpp
@@ -1296,6 +1296,83 @@
 }
 
 /**
+ * @brief Sets AssetTag
+ *
+ * @param[in] aResp   Shared pointer for generating response message.
+ * @param[in] assetTag  "AssetTag" from request.
+ *
+ * @return None.
+ */
+inline void setAssetTag(const std::shared_ptr<AsyncResp>& aResp,
+                        const std::string& assetTag)
+{
+    crow::connections::systemBus->async_method_call(
+        [aResp, assetTag](
+            const boost::system::error_code ec,
+            const std::vector<std::pair<
+                std::string,
+                std::vector<std::pair<std::string, std::vector<std::string>>>>>&
+                subtree) {
+            if (ec)
+            {
+                BMCWEB_LOG_DEBUG << "D-Bus response error on GetSubTree " << ec;
+                messages::internalError(aResp->res);
+                return;
+            }
+            if (subtree.size() == 0)
+            {
+                BMCWEB_LOG_DEBUG << "Can't find system D-Bus object!";
+                messages::internalError(aResp->res);
+                return;
+            }
+            // Assume only 1 system D-Bus object
+            // Throw an error if there is more than 1
+            if (subtree.size() > 1)
+            {
+                BMCWEB_LOG_DEBUG << "Found more than 1 system D-Bus object!";
+                messages::internalError(aResp->res);
+                return;
+            }
+            if (subtree[0].first.empty() || subtree[0].second.size() != 1)
+            {
+                BMCWEB_LOG_DEBUG << "Asset Tag Set mapper error!";
+                messages::internalError(aResp->res);
+                return;
+            }
+
+            const std::string& path = subtree[0].first;
+            const std::string& service = subtree[0].second.begin()->first;
+
+            if (service.empty())
+            {
+                BMCWEB_LOG_DEBUG << "Asset Tag Set service mapper error!";
+                messages::internalError(aResp->res);
+                return;
+            }
+
+            crow::connections::systemBus->async_method_call(
+                [aResp](const boost::system::error_code ec2) {
+                    if (ec2)
+                    {
+                        BMCWEB_LOG_DEBUG
+                            << "D-Bus response error on AssetTag Set " << ec2;
+                        messages::internalError(aResp->res);
+                        return;
+                    }
+                },
+                service, path, "org.freedesktop.DBus.Properties", "Set",
+                "xyz.openbmc_project.Inventory.Decorator.AssetTag", "AssetTag",
+                std::variant<std::string>(assetTag));
+        },
+        "xyz.openbmc_project.ObjectMapper",
+        "/xyz/openbmc_project/object_mapper",
+        "xyz.openbmc_project.ObjectMapper", "GetSubTree",
+        "/xyz/openbmc_project/inventory", int32_t(0),
+        std::array<const char*, 1>{
+            "xyz.openbmc_project.Inventory.Item.System"});
+}
+
+/**
  * @brief Sets automaticRetry (Auto Reboot)
  *
  * @param[in] aResp   Shared pointer for generating response message.
@@ -1984,18 +2061,25 @@
         std::optional<std::string> indicatorLed;
         std::optional<nlohmann::json> bootProps;
         std::optional<nlohmann::json> wdtTimerProps;
+        std::optional<std::string> assetTag;
         std::optional<std::string> powerRestorePolicy;
         auto asyncResp = std::make_shared<AsyncResp>(res);
 
         if (!json_util::readJson(req, res, "IndicatorLED", indicatorLed, "Boot",
                                  bootProps, "WatchdogTimer", wdtTimerProps,
-                                 "PowerRestorePolicy", powerRestorePolicy))
+                                 "PowerRestorePolicy", powerRestorePolicy,
+                                 "AssetTag", assetTag))
         {
             return;
         }
 
         res.result(boost::beast::http::status::no_content);
 
+        if (assetTag)
+        {
+            setAssetTag(asyncResp, *assetTag);
+        }
+
         if (wdtTimerProps)
         {
             std::optional<bool> wdtEnable;