json_utils: Add support jsonRead Patch/Action

Added support for readJson for Patch and Action. The only difference is
that Patch does not allow empty json input while Action does. Action with
empty input will use the default value based on the implementation and
return 200 OK response code.

readJsonPatch will replace the existing readJson and be used for path
requests. It will not allow empty json input and all requested
keys are required in the json input.

readJsonAction will be used for Action requests where it is possible for
all of the properties to be optional and allow empty request.
The optional properties are determined by the requested values type.

All current Action readJson are replaced with readJsonAction. It does
not change the existing behavior since it needs `std::optional`.
This will have to be updated later as we define the default behavior.

Tested:
Added unit tests and readJsonAction allows empty empty json object.

No Change to Redfish Tree.

Change-Id: Ia5e1f81695c528a20f1dc985aee19c920d8adaea
Signed-off-by: Willy Tu <wltu@google.com>
diff --git a/redfish-core/lib/account_service.hpp b/redfish-core/lib/account_service.hpp
index 232f51c..17f5fd3 100644
--- a/redfish-core/lib/account_service.hpp
+++ b/redfish-core/lib/account_service.hpp
@@ -1389,7 +1389,7 @@
                 std::optional<nlohmann::json> activeDirectoryObject;
                 std::optional<nlohmann::json> oemObject;
 
-                if (!json_util::readJson(
+                if (!json_util::readJsonPatch(
                         req, asyncResp->res, "AccountLockoutDuration",
                         unlockTimeout, "AccountLockoutThreshold",
                         lockoutThreshold, "MaxPasswordLength",
@@ -1578,9 +1578,9 @@
             std::string password;
             std::optional<std::string> roleId("User");
             std::optional<bool> enabled = true;
-            if (!json_util::readJson(req, asyncResp->res, "UserName", username,
-                                     "Password", password, "RoleId", roleId,
-                                     "Enabled", enabled))
+            if (!json_util::readJsonPatch(req, asyncResp->res, "UserName",
+                                          username, "Password", password,
+                                          "RoleId", roleId, "Enabled", enabled))
             {
                 return;
             }
@@ -1866,10 +1866,10 @@
                 if (userHasConfigureUsers)
                 {
                     // Users with ConfigureUsers can modify for all users
-                    if (!json_util::readJson(req, asyncResp->res, "UserName",
-                                             newUserName, "Password", password,
-                                             "RoleId", roleId, "Enabled",
-                                             enabled, "Locked", locked))
+                    if (!json_util::readJsonPatch(
+                            req, asyncResp->res, "UserName", newUserName,
+                            "Password", password, "RoleId", roleId, "Enabled",
+                            enabled, "Locked", locked))
                     {
                         return;
                     }
@@ -1883,8 +1883,8 @@
                         return;
                     }
                     // ConfigureSelf accounts can only modify their password
-                    if (!json_util::readJson(req, asyncResp->res, "Password",
-                                             password))
+                    if (!json_util::readJsonPatch(req, asyncResp->res,
+                                                  "Password", password))
                     {
                         return;
                     }
diff --git a/redfish-core/lib/certificate_service.hpp b/redfish-core/lib/certificate_service.hpp
index 4adaf0b..889f606 100644
--- a/redfish-core/lib/certificate_service.hpp
+++ b/redfish-core/lib/certificate_service.hpp
@@ -119,8 +119,9 @@
     std::string certificate;
     std::optional<std::string> certificateType = "PEM";
 
-    if (!json_util::readJson(reqJson, asyncResp->res, "CertificateString",
-                             certificate, "CertificateType", certificateType))
+    if (!json_util::readJsonPatch(req, asyncResp->res, "CertificateString",
+                                  certificate, "CertificateType",
+                                  certificateType))
     {
         BMCWEB_LOG_ERROR << "Required parameters are missing";
         messages::internalError(asyncResp->res);
@@ -272,7 +273,7 @@
                 std::vector<std::string>();
             std::optional<std::string> optSurname = "";
             std::optional<std::string> optUnstructuredName = "";
-            if (!json_util::readJson(
+            if (!json_util::readJsonAction(
                     req, asyncResp->res, "City", city, "CommonName", commonName,
                     "ContactPerson", optContactPerson, "Country", country,
                     "Organization", organization, "OrganizationalUnit",
@@ -683,10 +684,10 @@
             nlohmann::json certificateUri;
             std::optional<std::string> certificateType = "PEM";
 
-            if (!json_util::readJson(req, asyncResp->res, "CertificateString",
-                                     certificate, "CertificateUri",
-                                     certificateUri, "CertificateType",
-                                     certificateType))
+            if (!json_util::readJsonAction(req, asyncResp->res,
+                                           "CertificateString", certificate,
+                                           "CertificateUri", certificateUri,
+                                           "CertificateType", certificateType))
             {
                 BMCWEB_LOG_ERROR << "Required parameters are missing";
                 messages::internalError(asyncResp->res);
diff --git a/redfish-core/lib/chassis.hpp b/redfish-core/lib/chassis.hpp
index 1984d75..597e5d0 100644
--- a/redfish-core/lib/chassis.hpp
+++ b/redfish-core/lib/chassis.hpp
@@ -468,7 +468,7 @@
                 return;
             }
 
-            if (!json_util::readJson(
+            if (!json_util::readJsonPatch(
                     req, asyncResp->res, "LocationIndicatorActive",
                     locationIndicatorActive, "IndicatorLED", indicatorLed))
             {
@@ -661,8 +661,8 @@
 
                 std::string resetType;
 
-                if (!json_util::readJson(req, asyncResp->res, "ResetType",
-                                         resetType))
+                if (!json_util::readJsonAction(req, asyncResp->res, "ResetType",
+                                               resetType))
                 {
                     return;
                 }
diff --git a/redfish-core/lib/ethernet.hpp b/redfish-core/lib/ethernet.hpp
index 6770c48..227b4e9 100644
--- a/redfish-core/lib/ethernet.hpp
+++ b/redfish-core/lib/ethernet.hpp
@@ -1964,7 +1964,7 @@
                 DHCPParameters v4dhcpParms;
                 DHCPParameters v6dhcpParms;
 
-                if (!json_util::readJson(
+                if (!json_util::readJsonPatch(
                         req, asyncResp->res, "HostName", hostname, "FQDN", fqdn,
                         "IPv4StaticAddresses", ipv4StaticAddresses,
                         "MACAddress", macAddress, "StaticNameServers",
@@ -2163,8 +2163,8 @@
                 bool vlanEnable = false;
                 uint32_t vlanId = 0;
 
-                if (!json_util::readJson(req, asyncResp->res, "VLANEnable",
-                                         vlanEnable, "VLANId", vlanId))
+                if (!json_util::readJsonPatch(req, asyncResp->res, "VLANEnable",
+                                              vlanEnable, "VLANId", vlanId))
                 {
                     return;
                 }
@@ -2350,8 +2350,8 @@
                const std::string& rootInterfaceName) {
                 bool vlanEnable = false;
                 uint32_t vlanId = 0;
-                if (!json_util::readJson(req, asyncResp->res, "VLANId", vlanId,
-                                         "VLANEnable", vlanEnable))
+                if (!json_util::readJsonPatch(req, asyncResp->res, "VLANId",
+                                              vlanId, "VLANEnable", vlanEnable))
                 {
                     return;
                 }
diff --git a/redfish-core/lib/event_service.hpp b/redfish-core/lib/event_service.hpp
index 9ce2f05..b2b81fa 100644
--- a/redfish-core/lib/event_service.hpp
+++ b/redfish-core/lib/event_service.hpp
@@ -99,7 +99,7 @@
                 std::optional<uint32_t> retryAttemps;
                 std::optional<uint32_t> retryInterval;
 
-                if (!json_util::readJson(
+                if (!json_util::readJsonPatch(
                         req, asyncResp->res, "ServiceEnabled", serviceEnabled,
                         "DeliveryRetryAttempts", retryAttemps,
                         "DeliveryRetryIntervalSeconds", retryInterval))
@@ -223,7 +223,7 @@
                 std::optional<std::vector<nlohmann::json>> headers;
                 std::optional<std::vector<nlohmann::json>> mrdJsonArray;
 
-                if (!json_util::readJson(
+                if (!json_util::readJsonPatch(
                         req, asyncResp->res, "Destination", destUrl, "Context",
                         context, "Protocol", protocol, "SubscriptionType",
                         subscriptionType, "EventFormatType", eventFormatType2,
@@ -586,9 +586,10 @@
                 std::optional<std::string> retryPolicy;
                 std::optional<std::vector<nlohmann::json>> headers;
 
-                if (!json_util::readJson(req, asyncResp->res, "Context",
-                                         context, "DeliveryRetryPolicy",
-                                         retryPolicy, "HttpHeaders", headers))
+                if (!json_util::readJsonPatch(req, asyncResp->res, "Context",
+                                              context, "DeliveryRetryPolicy",
+                                              retryPolicy, "HttpHeaders",
+                                              headers))
                 {
                     return;
                 }
diff --git a/redfish-core/lib/hypervisor_system.hpp b/redfish-core/lib/hypervisor_system.hpp
index 645e39f..ba092c7 100644
--- a/redfish-core/lib/hypervisor_system.hpp
+++ b/redfish-core/lib/hypervisor_system.hpp
@@ -871,10 +871,10 @@
             std::optional<nlohmann::json> dhcpv4;
             std::optional<bool> ipv4DHCPEnabled;
 
-            if (!json_util::readJson(req, asyncResp->res, "HostName", hostName,
-                                     "IPv4StaticAddresses", ipv4StaticAddresses,
-                                     "IPv4Addresses", ipv4Addresses, "DHCPv4",
-                                     dhcpv4))
+            if (!json_util::readJsonPatch(req, asyncResp->res, "HostName",
+                                          hostName, "IPv4StaticAddresses",
+                                          ipv4StaticAddresses, "IPv4Addresses",
+                                          ipv4Addresses, "DHCPv4", dhcpv4))
             {
                 return;
             }
@@ -1038,8 +1038,8 @@
             [](const crow::Request& req,
                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
                 std::optional<std::string> resetType;
-                if (!json_util::readJson(req, asyncResp->res, "ResetType",
-                                         resetType))
+                if (!json_util::readJsonAction(req, asyncResp->res, "ResetType",
+                                               resetType))
                 {
                     // readJson adds appropriate error to response
                     return;
diff --git a/redfish-core/lib/log_services.hpp b/redfish-core/lib/log_services.hpp
index 9158591..94de5a9 100644
--- a/redfish-core/lib/log_services.hpp
+++ b/redfish-core/lib/log_services.hpp
@@ -790,7 +790,7 @@
     std::optional<std::string> diagnosticDataType;
     std::optional<std::string> oemDiagnosticDataType;
 
-    if (!redfish::json_util::readJson(
+    if (!redfish::json_util::readJsonAction(
             req, asyncResp->res, "DiagnosticDataType", diagnosticDataType,
             "OEMDiagnosticDataType", oemDiagnosticDataType))
     {
@@ -1625,8 +1625,8 @@
                const std::string& entryId) {
                 std::optional<bool> resolved;
 
-                if (!json_util::readJson(req, asyncResp->res, "Resolved",
-                                         resolved))
+                if (!json_util::readJsonPatch(req, asyncResp->res, "Resolved",
+                                              resolved))
                 {
                     return;
                 }
@@ -2854,7 +2854,7 @@
                          const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
             std::string diagnosticDataType;
             std::string oemDiagnosticDataType;
-            if (!redfish::json_util::readJson(
+            if (!redfish::json_util::readJsonAction(
                     req, asyncResp->res, "DiagnosticDataType",
                     diagnosticDataType, "OEMDiagnosticDataType",
                     oemDiagnosticDataType))
diff --git a/redfish-core/lib/managers.hpp b/redfish-core/lib/managers.hpp
index 59ae163..b3796a6 100644
--- a/redfish-core/lib/managers.hpp
+++ b/redfish-core/lib/managers.hpp
@@ -118,8 +118,8 @@
 
                 std::string resetType;
 
-                if (!json_util::readJson(req, asyncResp->res, "ResetType",
-                                         resetType))
+                if (!json_util::readJsonAction(req, asyncResp->res, "ResetType",
+                                               resetType))
                 {
                     return;
                 }
@@ -174,8 +174,8 @@
 
                 std::string resetType;
 
-                if (!json_util::readJson(req, asyncResp->res,
-                                         "ResetToDefaultsType", resetType))
+                if (!json_util::readJsonAction(
+                        req, asyncResp->res, "ResetToDefaultsType", resetType))
                 {
                     BMCWEB_LOG_DEBUG << "Missing property ResetToDefaultsType.";
 
@@ -2205,8 +2205,8 @@
             std::optional<nlohmann::json> links;
             std::optional<std::string> datetime;
 
-            if (!json_util::readJson(req, asyncResp->res, "Oem", oem,
-                                     "DateTime", datetime, "Links", links))
+            if (!json_util::readJsonPatch(req, asyncResp->res, "Oem", oem,
+                                          "DateTime", datetime, "Links", links))
             {
                 return;
             }
diff --git a/redfish-core/lib/metric_report_definition.hpp b/redfish-core/lib/metric_report_definition.hpp
index 4ac4c77..4e9d78a 100644
--- a/redfish-core/lib/metric_report_definition.hpp
+++ b/redfish-core/lib/metric_report_definition.hpp
@@ -143,10 +143,10 @@
     std::vector<nlohmann::json> metrics;
     std::vector<std::string> reportActions;
     std::optional<nlohmann::json> schedule;
-    if (!json_util::readJson(req, res, "Id", args.name, "Metrics", metrics,
-                             "MetricReportDefinitionType", args.reportingType,
-                             "ReportActions", reportActions, "Schedule",
-                             schedule))
+    if (!json_util::readJsonPatch(req, res, "Id", args.name, "Metrics", metrics,
+                                  "MetricReportDefinitionType",
+                                  args.reportingType, "ReportActions",
+                                  reportActions, "Schedule", schedule))
     {
         return false;
     }
diff --git a/redfish-core/lib/network_protocol.hpp b/redfish-core/lib/network_protocol.hpp
index eda9915..e221d96 100644
--- a/redfish-core/lib/network_protocol.hpp
+++ b/redfish-core/lib/network_protocol.hpp
@@ -408,9 +408,9 @@
             std::optional<nlohmann::json> ipmi;
             std::optional<nlohmann::json> ssh;
 
-            if (!json_util::readJson(req, asyncResp->res, "NTP", ntp,
-                                     "HostName", newHostName, "IPMI", ipmi,
-                                     "SSH", ssh))
+            if (!json_util::readJsonPatch(req, asyncResp->res, "NTP", ntp,
+                                          "HostName", newHostName, "IPMI", ipmi,
+                                          "SSH", ssh))
             {
                 return;
             }
diff --git a/redfish-core/lib/power.hpp b/redfish-core/lib/power.hpp
index 48f22b7..694d29e 100644
--- a/redfish-core/lib/power.hpp
+++ b/redfish-core/lib/power.hpp
@@ -323,9 +323,9 @@
                 std::optional<std::vector<nlohmann::json>> voltageCollections;
                 std::optional<std::vector<nlohmann::json>> powerCtlCollections;
 
-                if (!json_util::readJson(req, sensorAsyncResp->asyncResp->res,
-                                         "PowerControl", powerCtlCollections,
-                                         "Voltages", voltageCollections))
+                if (!json_util::readJsonPatch(
+                        req, sensorAsyncResp->asyncResp->res, "PowerControl",
+                        powerCtlCollections, "Voltages", voltageCollections))
                 {
                     return;
                 }
diff --git a/redfish-core/lib/processor.hpp b/redfish-core/lib/processor.hpp
index b51a901..654a08b 100644
--- a/redfish-core/lib/processor.hpp
+++ b/redfish-core/lib/processor.hpp
@@ -1226,9 +1226,9 @@
                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
                const std::string& processorId) {
                 std::optional<nlohmann::json> appliedConfigJson;
-                if (!json_util::readJson(req, asyncResp->res,
-                                         "AppliedOperatingConfig",
-                                         appliedConfigJson))
+                if (!json_util::readJsonPatch(req, asyncResp->res,
+                                              "AppliedOperatingConfig",
+                                              appliedConfigJson))
                 {
                     return;
                 }
diff --git a/redfish-core/lib/redfish_sessions.hpp b/redfish-core/lib/redfish_sessions.hpp
index 234a524..1568c00 100644
--- a/redfish-core/lib/redfish_sessions.hpp
+++ b/redfish-core/lib/redfish_sessions.hpp
@@ -147,9 +147,9 @@
                 std::string password;
                 std::optional<nlohmann::json> oemObject;
                 std::string clientId;
-                if (!json_util::readJson(req, asyncResp->res, "UserName",
-                                         username, "Password", password, "Oem",
-                                         oemObject))
+                if (!json_util::readJsonPatch(req, asyncResp->res, "UserName",
+                                              username, "Password", password,
+                                              "Oem", oemObject))
                 {
                     return;
                 }
@@ -246,8 +246,8 @@
             [](const crow::Request& req,
                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) -> void {
                 std::optional<int64_t> sessionTimeout;
-                if (!json_util::readJson(req, asyncResp->res, "SessionTimeout",
-                                         sessionTimeout))
+                if (!json_util::readJsonPatch(req, asyncResp->res,
+                                              "SessionTimeout", sessionTimeout))
                 {
                     return;
                 }
diff --git a/redfish-core/lib/systems.hpp b/redfish-core/lib/systems.hpp
index 62bd11b..0d1e12d 100644
--- a/redfish-core/lib/systems.hpp
+++ b/redfish-core/lib/systems.hpp
@@ -2765,8 +2765,8 @@
                 post)([](const crow::Request& req,
                          const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
             std::string resetType;
-            if (!json_util::readJson(req, asyncResp->res, "ResetType",
-                                     resetType))
+            if (!json_util::readJsonAction(req, asyncResp->res, "ResetType",
+                                           resetType))
             {
                 return;
             }
@@ -3020,7 +3020,7 @@
                 std::optional<std::string> powerRestorePolicy;
                 std::optional<std::string> powerMode;
                 std::optional<nlohmann::json> ipsProps;
-                if (!json_util::readJson(
+                if (!json_util::readJsonPatch(
                         req, asyncResp->res, "IndicatorLED", indicatorLed,
                         "LocationIndicatorActive", locationIndicatorActive,
                         "Boot", bootProps, "WatchdogTimer", wdtTimerProps,
diff --git a/redfish-core/lib/thermal.hpp b/redfish-core/lib/thermal.hpp
index feb78a0..c49bb0b 100644
--- a/redfish-core/lib/thermal.hpp
+++ b/redfish-core/lib/thermal.hpp
@@ -71,9 +71,9 @@
                     asyncResp, chassisName, thermalPaths->second,
                     sensors::node::thermal);
 
-                if (!json_util::readJson(req, sensorsAsyncResp->asyncResp->res,
-                                         "Temperatures", temperatureCollections,
-                                         "Fans", fanCollections))
+                if (!json_util::readJsonPatch(
+                        req, sensorsAsyncResp->asyncResp->res, "Temperatures",
+                        temperatureCollections, "Fans", fanCollections))
                 {
                     return;
                 }
diff --git a/redfish-core/lib/update_service.hpp b/redfish-core/lib/update_service.hpp
index 4b18520..1288815 100644
--- a/redfish-core/lib/update_service.hpp
+++ b/redfish-core/lib/update_service.hpp
@@ -421,8 +421,9 @@
             // 1) TransferProtocol:TFTP ImageURI:1.1.1.1/myfile.bin
             // 2) ImageURI:tftp://1.1.1.1/myfile.bin
 
-            if (!json_util::readJson(req, asyncResp->res, "TransferProtocol",
-                                     transferProtocol, "ImageURI", imageURI))
+            if (!json_util::readJsonAction(req, asyncResp->res,
+                                           "TransferProtocol", transferProtocol,
+                                           "ImageURI", imageURI))
             {
                 BMCWEB_LOG_DEBUG
                     << "Missing TransferProtocol or ImageURI parameter";
@@ -599,8 +600,8 @@
             BMCWEB_LOG_DEBUG << "doPatch...";
 
             std::optional<nlohmann::json> pushUriOptions;
-            if (!json_util::readJson(req, asyncResp->res, "HttpPushUriOptions",
-                                     pushUriOptions))
+            if (!json_util::readJsonPatch(req, asyncResp->res,
+                                          "HttpPushUriOptions", pushUriOptions))
             {
                 return;
             }
diff --git a/redfish-core/lib/virtual_media.hpp b/redfish-core/lib/virtual_media.hpp
index 9e44e80..049cc56 100644
--- a/redfish-core/lib/virtual_media.hpp
+++ b/redfish-core/lib/virtual_media.hpp
@@ -796,7 +796,7 @@
 
                 // Read obligatory parameters (url of
                 // image)
-                if (!json_util::readJson(
+                if (!json_util::readJsonAction(
                         req, asyncResp->res, "Image", actionParams.imageUrl,
                         "WriteProtected", actionParams.writeProtected,
                         "UserName", actionParams.userName, "Password",