Refactor multipart update parameter extraction

This commit extracts helper functions to improve code readability:
- parseFormPartName(): Parses the 'name' parameter from Content-
Disposition
- processUpdateParameters(): Handles UpdateParameters JSON processing

The extractMultipartUpdateParameters() function is refactored to use
these helpers, making the code more modular and easier to understand.

This is a pure refactoring with no functional changes.

Change-Id: I9bfd3c92666bbe362512d9fdd9472a4e44ad2241
Signed-off-by: Ed Tanous <etanous@nvidia.com>
Signed-off-by: Rajeev Ranjan <ranjan.rajeev1609@gmail.com>
diff --git a/redfish-core/lib/update_service.hpp b/redfish-core/lib/update_service.hpp
index 0ac36b5..18abc2e 100644
--- a/redfish-core/lib/update_service.hpp
+++ b/redfish-core/lib/update_service.hpp
@@ -684,11 +684,14 @@
                     "RequestedApplyTime", applyTimeNewVal);
 }
 
-struct MultiPartUpdateParameters
+struct MultiPartUpdate
 {
-    std::optional<std::string> applyTime;
     std::string uploadData;
-    std::vector<std::string> targets;
+    struct UpdateParameters
+    {
+        std::optional<std::string> applyTime;
+        std::vector<std::string> targets;
+    } params;
 };
 
 inline std::optional<std::string> processUrl(
@@ -718,12 +721,82 @@
     return std::make_optional(firmwareId);
 }
 
-inline std::optional<MultiPartUpdateParameters>
-    extractMultipartUpdateParameters(
-        const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-        MultipartParser parser)
+inline std::optional<std::string> parseFormPartName(
+    const boost::beast::http::fields::const_iterator& contentDisposition)
 {
-    MultiPartUpdateParameters multiRet;
+    size_t semicolonPos = contentDisposition->value().find(';');
+    if (semicolonPos == std::string::npos)
+    {
+        return std::nullopt;
+    }
+
+    for (const auto& param : boost::beast::http::param_list{
+             contentDisposition->value().substr(semicolonPos)})
+    {
+        if (param.first == "name" && !param.second.empty())
+        {
+            return std::string(param.second);
+        }
+    }
+    return std::nullopt;
+}
+
+inline std::optional<MultiPartUpdate::UpdateParameters> processUpdateParameters(
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    std::string_view content)
+{
+    MultiPartUpdate::UpdateParameters multiRet;
+    nlohmann::json jsonContent = nlohmann::json::parse(content, nullptr, false);
+    if (jsonContent.is_discarded())
+    {
+        return std::nullopt;
+    }
+    nlohmann::json::object_t* obj =
+        jsonContent.get_ptr<nlohmann::json::object_t*>();
+    if (obj == nullptr)
+    {
+        messages::propertyValueTypeError(asyncResp->res, content,
+                                         "UpdateParameters");
+        return std::nullopt;
+    }
+
+    std::vector<std::string> tempTargets;
+    if (!json_util::readJsonObject(                            //
+            *obj, asyncResp->res,                              //
+            "@Redfish.OperationApplyTime", multiRet.applyTime, //
+            "Targets", tempTargets                             //
+            ))
+    {
+        return std::nullopt;
+    }
+
+    for (size_t urlIndex = 0; urlIndex < tempTargets.size(); urlIndex++)
+    {
+        const std::string& target = tempTargets[urlIndex];
+        boost::system::result<boost::urls::url_view> url =
+            boost::urls::parse_origin_form(target);
+        auto res = processUrl(url);
+        if (!res.has_value())
+        {
+            messages::propertyValueFormatError(
+                asyncResp->res, target, std::format("Targets/{}", urlIndex));
+            return std::nullopt;
+        }
+        multiRet.targets.emplace_back(res.value());
+    }
+    if (multiRet.targets.size() != 1)
+    {
+        messages::propertyValueFormatError(asyncResp->res, multiRet.targets,
+                                           "Targets");
+        return std::nullopt;
+    }
+    return multiRet;
+}
+
+inline std::optional<MultiPartUpdate> extractMultipartUpdateParameters(
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, MultipartParser parser)
+{
+    MultiPartUpdate multiRet;
     for (FormPart& formpart : parser.mime_fields)
     {
         boost::beast::http::fields::const_iterator it =
@@ -735,75 +808,27 @@
         }
         BMCWEB_LOG_INFO("Parsing value {}", it->value());
 
-        // The construction parameters of param_list must start with `;`
-        size_t index = it->value().find(';');
-        if (index == std::string::npos)
+        auto formFieldNameOpt = parseFormPartName(it);
+        if (!formFieldNameOpt.has_value())
         {
             continue;
         }
 
-        for (const auto& param :
-             boost::beast::http::param_list{it->value().substr(index)})
+        const std::string& formFieldName = formFieldNameOpt.value();
+
+        if (formFieldName == "UpdateParameters")
         {
-            if (param.first != "name" || param.second.empty())
+            std::optional<MultiPartUpdate::UpdateParameters> params =
+                processUpdateParameters(asyncResp, formpart.content);
+            if (!params)
             {
-                continue;
+                return std::nullopt;
             }
-
-            if (param.second == "UpdateParameters")
-            {
-                std::vector<std::string> tempTargets;
-                nlohmann::json content =
-                    nlohmann::json::parse(formpart.content, nullptr, false);
-                if (content.is_discarded())
-                {
-                    return std::nullopt;
-                }
-                nlohmann::json::object_t* obj =
-                    content.get_ptr<nlohmann::json::object_t*>();
-                if (obj == nullptr)
-                {
-                    messages::propertyValueTypeError(
-                        asyncResp->res, formpart.content, "UpdateParameters");
-                    return std::nullopt;
-                }
-
-                if (!json_util::readJsonObject(                            //
-                        *obj, asyncResp->res,                              //
-                        "@Redfish.OperationApplyTime", multiRet.applyTime, //
-                        "Targets", tempTargets                             //
-                        ))
-                {
-                    return std::nullopt;
-                }
-
-                for (size_t urlIndex = 0; urlIndex < tempTargets.size();
-                     urlIndex++)
-                {
-                    const std::string& target = tempTargets[urlIndex];
-                    boost::system::result<boost::urls::url_view> url =
-                        boost::urls::parse_origin_form(target);
-                    auto res = processUrl(url);
-                    if (!res.has_value())
-                    {
-                        messages::propertyValueFormatError(
-                            asyncResp->res, target,
-                            std::format("Targets/{}", urlIndex));
-                        return std::nullopt;
-                    }
-                    multiRet.targets.emplace_back(res.value());
-                }
-                if (multiRet.targets.size() != 1)
-                {
-                    messages::propertyValueFormatError(
-                        asyncResp->res, multiRet.targets, "Targets");
-                    return std::nullopt;
-                }
-            }
-            else if (param.second == "UpdateFile")
-            {
-                multiRet.uploadData = std::move(formpart.content);
-            }
+            multiRet.params = std::move(*params);
+        }
+        else if (formFieldName == "UpdateFile")
+        {
+            multiRet.uploadData = std::move(formpart.content);
         }
     }
 
@@ -813,7 +838,7 @@
         messages::propertyMissing(asyncResp->res, "UpdateFile");
         return std::nullopt;
     }
-    if (multiRet.targets.empty())
+    if (multiRet.params.targets.empty())
     {
         messages::propertyMissing(asyncResp->res, "Targets");
         return std::nullopt;
@@ -979,21 +1004,21 @@
     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
     const crow::Request& req, MultipartParser&& parser)
 {
-    std::optional<MultiPartUpdateParameters> multipart =
+    std::optional<MultiPartUpdate> multipart =
         extractMultipartUpdateParameters(asyncResp, std::move(parser));
     if (!multipart)
     {
         return;
     }
-    if (!multipart->applyTime)
+    if (!multipart->params.applyTime)
     {
-        multipart->applyTime = "OnReset";
+        multipart->params.applyTime = "OnReset";
     }
 
     if constexpr (BMCWEB_REDFISH_UPDATESERVICE_USE_DBUS)
     {
         std::string applyTimeNewVal;
-        if (!convertApplyTime(asyncResp->res, *multipart->applyTime,
+        if (!convertApplyTime(asyncResp->res, *multipart->params.applyTime,
                               applyTimeNewVal))
         {
             return;
@@ -1002,11 +1027,11 @@
 
         processUpdateRequest(asyncResp, std::move(payload),
                              multipart->uploadData, applyTimeNewVal,
-                             multipart->targets);
+                             multipart->params.targets);
     }
     else
     {
-        setApplyTime(asyncResp, *multipart->applyTime);
+        setApplyTime(asyncResp, *multipart->params.applyTime);
 
         // Setup callback for when new software detected
         monitorForSoftwareAvailable(asyncResp, req,