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/http/http_request.hpp b/http/http_request.hpp
index e0cb9dc..cb7b71b 100644
--- a/http/http_request.hpp
+++ b/http/http_request.hpp
@@ -26,7 +26,7 @@
bool isSecure{false};
- const std::string& body;
+ std::string& body;
boost::asio::io_context* ioService{};
boost::asio::ip::address ipAddress{};
diff --git a/include/ibm/management_console_rest.hpp b/include/ibm/management_console_rest.hpp
index 39146c9..a9e0ec4 100644
--- a/include/ibm/management_console_rest.hpp
+++ b/include/ibm/management_console_rest.hpp
@@ -366,8 +366,8 @@
{
std::string broadcastMsg;
- if (!redfish::json_util::readJson(req, asyncResp->res, "Message",
- broadcastMsg))
+ if (!redfish::json_util::readJsonPatch(req, asyncResp->res, "Message",
+ broadcastMsg))
{
BMCWEB_LOG_DEBUG << "Not a Valid JSON";
asyncResp->res.result(boost::beast::http::status::bad_request);
@@ -747,8 +747,8 @@
[](const crow::Request& req,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
std::vector<nlohmann::json> body;
- if (!redfish::json_util::readJson(req, asyncResp->res,
- "Request", body))
+ if (!redfish::json_util::readJsonAction(req, asyncResp->res,
+ "Request", body))
{
BMCWEB_LOG_DEBUG << "Not a Valid JSON";
asyncResp->res.result(
@@ -765,9 +765,9 @@
std::string type;
std::vector<uint32_t> listTransactionIds;
- if (!redfish::json_util::readJson(req, asyncResp->res, "Type",
- type, "TransactionIDs",
- listTransactionIds))
+ if (!redfish::json_util::readJsonPatch(
+ req, asyncResp->res, "Type", type, "TransactionIDs",
+ listTransactionIds))
{
asyncResp->res.result(
boost::beast::http::status::bad_request);
@@ -796,8 +796,8 @@
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
ListOfSessionIds listSessionIds;
- if (!redfish::json_util::readJson(req, asyncResp->res,
- "SessionIDs", listSessionIds))
+ if (!redfish::json_util::readJsonPatch(
+ req, asyncResp->res, "SessionIDs", listSessionIds))
{
asyncResp->res.result(
boost::beast::http::status::bad_request);
diff --git a/meson.build b/meson.build
index 24777e8..9ad4d6d 100644
--- a/meson.build
+++ b/meson.build
@@ -380,6 +380,7 @@
'include/ut/multipart_test.cpp',
'redfish-core/ut/configfile_test.cpp',
'redfish-core/ut/hex_utils_test.cpp',
+ 'redfish-core/ut/json_utils_test.cpp',
'redfish-core/ut/lock_test.cpp',
'redfish-core/ut/privileges_test.cpp',
'redfish-core/ut/registries_test.cpp',
diff --git a/redfish-core/include/utils/json_utils.hpp b/redfish-core/include/utils/json_utils.hpp
index 22577ea..21701c7 100644
--- a/redfish-core/include/utils/json_utils.hpp
+++ b/redfish-core/include/utils/json_utils.hpp
@@ -353,6 +353,9 @@
bool ret = true;
if (key != keyToCheck)
{
+ // key is an element at root and should cause extra element error.
+ // If we are requesting elements that is under key like key/other,
+ // ignore the extra element error.
ret =
readJsonValues<Count, Index + 1>(
key, jsonValue, res, handled,
@@ -389,6 +392,7 @@
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
return details::handleMissing<Index + 1, Count>(handled, res, in...) && ret;
}
+
} // namespace details
template <typename... UnpackTypes>
@@ -403,13 +407,6 @@
return false;
}
- if (jsonRequest.empty())
- {
- BMCWEB_LOG_DEBUG << "Json value is empty";
- messages::emptyJSON(res);
- return false;
- }
-
std::bitset<(sizeof...(in) + 1) / 2> handled(0);
for (const auto& item : jsonRequest.items())
{
@@ -425,8 +422,8 @@
}
template <typename... UnpackTypes>
-bool readJson(const crow::Request& req, crow::Response& res, const char* key,
- UnpackTypes&... in)
+bool readJsonPatch(const crow::Request& req, crow::Response& res,
+ const char* key, UnpackTypes&... in)
{
nlohmann::json jsonRequest;
if (!json_util::processJsonFromRequest(res, req, jsonRequest))
@@ -434,6 +431,28 @@
BMCWEB_LOG_DEBUG << "Json value not readable";
return false;
}
+
+ if (jsonRequest.empty())
+ {
+ BMCWEB_LOG_DEBUG << "Json value is empty";
+ messages::emptyJSON(res);
+ return false;
+ }
+
+ return readJson(jsonRequest, res, key, in...);
+}
+
+template <typename... UnpackTypes>
+bool readJsonAction(const crow::Request& req, crow::Response& res,
+ const char* key, UnpackTypes&... in)
+{
+ nlohmann::json jsonRequest;
+ if (!json_util::processJsonFromRequest(res, req, jsonRequest))
+ {
+ BMCWEB_LOG_DEBUG << "Json value not readable";
+ return false;
+ }
+
return readJson(jsonRequest, res, key, in...);
}
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",
diff --git a/redfish-core/ut/json_utils_test.cpp b/redfish-core/ut/json_utils_test.cpp
new file mode 100644
index 0000000..1c11755
--- /dev/null
+++ b/redfish-core/ut/json_utils_test.cpp
@@ -0,0 +1,244 @@
+#include "utils/json_utils.hpp"
+
+#include <string>
+#include <vector>
+
+#include <gmock/gmock.h>
+
+using redfish::json_util::readJson;
+using redfish::json_util::readJsonAction;
+using redfish::json_util::readJsonPatch;
+
+TEST(readJson, ValidElements)
+{
+ crow::Response res;
+ nlohmann::json jsonRequest = {{"integer", 1},
+ {"string", "hello"},
+ {"vector", std::vector<uint64_t>{1, 2, 3}}};
+
+ int64_t integer = 0;
+ std::string str;
+ std::vector<uint64_t> vec;
+ EXPECT_TRUE(readJson(jsonRequest, res, "integer", integer, "string", str,
+ "vector", vec));
+ EXPECT_EQ(res.result(), boost::beast::http::status::ok);
+ EXPECT_TRUE(res.jsonValue.empty());
+
+ EXPECT_EQ(integer, 1);
+ EXPECT_EQ(str, "hello");
+ EXPECT_TRUE((vec == std::vector<uint64_t>{1, 2, 3}));
+}
+
+TEST(readJson, ExtraElements)
+{
+ crow::Response res;
+ nlohmann::json jsonRequest = {{"integer", 1}, {"string0", "hello"}};
+
+ int64_t integer = 0;
+ std::string str;
+ EXPECT_FALSE(readJson(jsonRequest, res, "integer", integer));
+ EXPECT_EQ(res.result(), boost::beast::http::status::bad_request);
+ EXPECT_FALSE(res.jsonValue.empty());
+
+ EXPECT_FALSE(readJson(jsonRequest, res, "string0", str));
+ EXPECT_EQ(res.result(), boost::beast::http::status::bad_request);
+ EXPECT_FALSE(res.jsonValue.empty());
+}
+
+TEST(readJson, WrongElementType)
+{
+ crow::Response res;
+ nlohmann::json jsonRequest = {{"integer", 1}, {"string0", "hello"}};
+
+ int64_t integer = 0;
+ std::string str0;
+ std::string str1;
+ EXPECT_FALSE(readJson(jsonRequest, res, "integer", str0));
+ EXPECT_EQ(res.result(), boost::beast::http::status::bad_request);
+ EXPECT_FALSE(res.jsonValue.empty());
+
+ EXPECT_FALSE(readJson(jsonRequest, res, "string0", integer));
+ EXPECT_EQ(res.result(), boost::beast::http::status::bad_request);
+ EXPECT_FALSE(res.jsonValue.empty());
+
+ EXPECT_FALSE(
+ readJson(jsonRequest, res, "integer", str0, "string0", integer));
+ EXPECT_EQ(res.result(), boost::beast::http::status::bad_request);
+ EXPECT_FALSE(res.jsonValue.empty());
+}
+
+TEST(readJson, MissingElement)
+{
+ crow::Response res;
+ nlohmann::json jsonRequest = {{"integer", 1}, {"string0", "hello"}};
+
+ int64_t integer = 0;
+ std::string str0;
+ std::string str1;
+ std::vector<uint8_t> vec;
+ EXPECT_FALSE(readJson(jsonRequest, res, "integer", integer, "string0", str0,
+ "vector", vec));
+ EXPECT_EQ(res.result(), boost::beast::http::status::bad_request);
+ EXPECT_FALSE(res.jsonValue.empty());
+
+ EXPECT_FALSE(readJson(jsonRequest, res, "integer", integer, "string0", str0,
+ "string1", str1));
+ EXPECT_EQ(res.result(), boost::beast::http::status::bad_request);
+ EXPECT_FALSE(res.jsonValue.empty());
+}
+
+TEST(readJson, JsonVector)
+{
+ crow::Response res;
+ nlohmann::json jsonRequest = R"(
+ {
+ "TestJson": [{"hello": "yes"}, [{"there": "no"}, "nice"]]
+ }
+ )"_json;
+
+ std::vector<nlohmann::json> jsonVec;
+ EXPECT_TRUE(readJson(jsonRequest, res, "TestJson", jsonVec));
+ EXPECT_EQ(res.result(), boost::beast::http::status::ok);
+ EXPECT_TRUE(res.jsonValue.empty());
+}
+
+TEST(readJson, ExtraElement)
+{
+ crow::Response res;
+ nlohmann::json jsonRequest = {{"integer", 1}, {"string", "hello"}};
+
+ std::optional<int> integer;
+ std::optional<std::string> str;
+ std::optional<std::vector<uint8_t>> vec;
+
+ EXPECT_FALSE(readJson(jsonRequest, res, "integer", integer));
+ EXPECT_EQ(res.result(), boost::beast::http::status::bad_request);
+ EXPECT_FALSE(res.jsonValue.empty());
+ EXPECT_EQ(integer, 1);
+
+ EXPECT_FALSE(readJson(jsonRequest, res, "string", str));
+ EXPECT_EQ(res.result(), boost::beast::http::status::bad_request);
+ EXPECT_FALSE(res.jsonValue.empty());
+ EXPECT_EQ(str, "hello");
+}
+
+TEST(readJson, ValidMissingElement)
+{
+ crow::Response res;
+ nlohmann::json jsonRequest = {{"integer", 1}};
+
+ std::optional<int> integer;
+ int requiredInteger = 0;
+ std::optional<std::string> str0;
+ std::optional<std::string> str1;
+ std::optional<std::vector<uint8_t>> vec;
+ EXPECT_TRUE(readJson(jsonRequest, res, "missing_integer", integer,
+ "integer", requiredInteger));
+ EXPECT_EQ(res.result(), boost::beast::http::status::ok);
+ EXPECT_TRUE(res.jsonValue.empty());
+ EXPECT_EQ(integer, std::nullopt);
+
+ EXPECT_TRUE(readJson(jsonRequest, res, "missing_string", str0, "integer",
+ requiredInteger));
+ EXPECT_EQ(res.result(), boost::beast::http::status::ok);
+ EXPECT_TRUE(res.jsonValue.empty());
+ EXPECT_EQ(str0, std::nullopt);
+
+ EXPECT_TRUE(readJson(jsonRequest, res, "integer", integer, "string", str0,
+ "vector", vec));
+ EXPECT_EQ(res.result(), boost::beast::http::status::ok);
+ EXPECT_TRUE(res.jsonValue.empty());
+ EXPECT_EQ(integer, 1);
+ EXPECT_EQ(str0, std::nullopt);
+ EXPECT_EQ(vec, std::nullopt);
+
+ EXPECT_TRUE(readJson(jsonRequest, res, "integer", integer, "string0", str0,
+ "missing_string", str1));
+ EXPECT_EQ(res.result(), boost::beast::http::status::ok);
+ EXPECT_TRUE(res.jsonValue.empty());
+ EXPECT_EQ(str1, std::nullopt);
+}
+
+TEST(readJson, InvalidMissingElement)
+{
+ crow::Response res;
+ nlohmann::json jsonRequest = {{"integer", 1}, {"string", "hello"}};
+
+ int integer = 0;
+ std::string str0;
+ std::string str1;
+ std::vector<uint8_t> vec;
+ EXPECT_FALSE(readJson(jsonRequest, res, "missing_integer", integer));
+ EXPECT_EQ(res.result(), boost::beast::http::status::bad_request);
+ EXPECT_FALSE(res.jsonValue.empty());
+
+ EXPECT_FALSE(readJson(jsonRequest, res, "missing_string", str0));
+ EXPECT_EQ(res.result(), boost::beast::http::status::bad_request);
+ EXPECT_FALSE(res.jsonValue.empty());
+
+ EXPECT_FALSE(readJson(jsonRequest, res, "integer", integer, "string", str0,
+ "vector", vec));
+ EXPECT_EQ(res.result(), boost::beast::http::status::bad_request);
+ EXPECT_FALSE(res.jsonValue.empty());
+
+ EXPECT_FALSE(readJson(jsonRequest, res, "integer", integer, "string0", str0,
+ "missing_string", str1));
+ EXPECT_EQ(res.result(), boost::beast::http::status::bad_request);
+ EXPECT_FALSE(res.jsonValue.empty());
+}
+
+TEST(readJsonPatch, ValidElements)
+{
+ crow::Response res;
+ std::error_code ec;
+ crow::Request req({}, ec);
+ // Ignore errors intentionally
+ req.body = "{\"integer\": 1}";
+
+ int64_t integer = 0;
+ EXPECT_TRUE(readJsonPatch(req, res, "integer", integer));
+ EXPECT_EQ(res.result(), boost::beast::http::status::ok);
+ EXPECT_TRUE(res.jsonValue.empty());
+}
+
+TEST(readJsonPatch, EmptyObjectDisallowed)
+{
+ crow::Response res;
+ std::error_code ec;
+ crow::Request req({}, ec);
+ // Ignore errors intentionally
+ req.body = "{}";
+
+ std::optional<int64_t> integer = 0;
+ EXPECT_FALSE(readJsonPatch(req, res, "integer", integer));
+ EXPECT_EQ(res.result(), boost::beast::http::status::bad_request);
+ EXPECT_FALSE(res.jsonValue.empty());
+}
+
+TEST(readJsonAction, ValidElements)
+{
+ crow::Response res;
+ std::error_code ec;
+ crow::Request req({}, ec);
+ // Ignore errors intentionally
+ req.body = "{\"integer\": 1}";
+
+ int64_t integer = 0;
+ EXPECT_TRUE(readJsonAction(req, res, "integer", integer));
+ EXPECT_EQ(res.result(), boost::beast::http::status::ok);
+ EXPECT_TRUE(res.jsonValue.empty());
+}
+
+TEST(readJsonAction, EmptyObjectAllowed)
+{
+ crow::Response res;
+ std::error_code ec;
+ crow::Request req({}, ec);
+ // Ignore errors intentionally
+ req.body = "{}";
+
+ std::optional<int64_t> integer = 0;
+ EXPECT_TRUE(readJsonAction(req, res, "integer", integer));
+ EXPECT_EQ(res.result(), boost::beast::http::status::ok);
+ EXPECT_TRUE(res.jsonValue.empty());
+}