Add automatic range-checking and type-casting for readJson
This change allows numerical types to be passed to readJson
with the desired type. They will then be automatically
range-checked and cast to that type.
Tested: Requested variables of type uint64_t, uint8_t, int,
int16_t, and int8_t and confirmed that the range was correctly
checked for each.
Change-Id: Ia55cb03ba2c4321a7de35a7c7b566980a72c5449
Signed-off-by: Jason M. Bills <jason.m.bills@linux.intel.com>
diff --git a/redfish-core/include/utils/json_utils.hpp b/redfish-core/include/utils/json_utils.hpp
index e56a0d6..778a905 100644
--- a/redfish-core/include/utils/json_utils.hpp
+++ b/redfish-core/include/utils/json_utils.hpp
@@ -42,19 +42,74 @@
nlohmann::json& reqJson);
namespace details
{
-template <typename Type> struct unpackValue
+
+template <typename Type> struct is_optional : std::false_type
{
- using isRequired = std::true_type;
- using JsonType = std::add_const_t<std::add_pointer_t<Type>>;
};
-template <typename OptionalType>
-struct unpackValue<boost::optional<OptionalType>>
+template <typename Type>
+struct is_optional<boost::optional<Type>> : std::true_type
{
- using isRequired = std::false_type;
- using JsonType = std::add_const_t<std::add_pointer_t<OptionalType>>;
};
+template <typename Type>
+constexpr bool is_optional_v = is_optional<Type>::value;
+
+template <typename Type>
+void unpackValue(nlohmann::json& jsonValue, const std::string& key,
+ crow::Response& res, Type& value)
+{
+ if constexpr (std::is_arithmetic_v<Type>)
+ {
+ using NumType =
+ std::conditional_t<std::is_signed_v<Type>, int64_t, uint64_t>;
+
+ NumType* jsonPtr = jsonValue.get_ptr<NumType*>();
+ if (jsonPtr == nullptr)
+ {
+ BMCWEB_LOG_DEBUG
+ << "Value for key " << key
+ << " was incorrect type: " << jsonValue.type_name();
+ messages::propertyValueTypeError(res, jsonValue.dump(), key);
+ return;
+ }
+ if (*jsonPtr > std::numeric_limits<Type>::max())
+ {
+ BMCWEB_LOG_DEBUG << "Value for key " << key
+ << " was out of range: " << jsonValue.type_name();
+ messages::propertyValueNotInList(res, jsonValue.dump(), key);
+ return;
+ }
+ if (*jsonPtr < std::numeric_limits<Type>::min())
+ {
+ BMCWEB_LOG_DEBUG << "Value for key " << key
+ << " was out of range: " << jsonValue.type_name();
+ messages::propertyValueNotInList(res, jsonValue.dump(), key);
+ return;
+ }
+ value = static_cast<Type>(*jsonPtr);
+ }
+ else if constexpr (is_optional_v<Type>)
+ {
+ value.emplace();
+ unpackValue<typename Type::value_type>(jsonValue, key, res, *value);
+ }
+ else
+ {
+ using JsonType = std::add_const_t<std::add_pointer_t<Type>>;
+ JsonType jsonPtr = jsonValue.get_ptr<JsonType>();
+ if (jsonPtr == nullptr)
+ {
+ BMCWEB_LOG_DEBUG
+ << "Value for key " << key
+ << " was incorrect type: " << jsonValue.type_name();
+ messages::propertyValueTypeError(res, jsonValue.dump(), key);
+ return;
+ }
+ value = std::move(*jsonPtr);
+ }
+}
+
template <size_t Count, size_t Index>
void readJsonValues(const std::string& key, nlohmann::json& jsonValue,
crow::Response& res, std::bitset<Count>& handled)
@@ -78,17 +133,7 @@
handled.set(Index);
- using UnpackType = typename unpackValue<ValueType>::JsonType;
- UnpackType value = jsonValue.get_ptr<UnpackType>();
- if (value == nullptr)
- {
- BMCWEB_LOG_DEBUG << "Value for key " << key
- << " was incorrect type: " << jsonValue.type_name();
- messages::propertyValueTypeError(res, jsonValue.dump(), key);
- return;
- }
-
- valueToFill = *value;
+ unpackValue<ValueType>(jsonValue, key, res, valueToFill);
}
template <size_t Index = 0, size_t Count>
@@ -101,7 +146,7 @@
void handleMissing(std::bitset<Count>& handled, crow::Response& res,
const char* key, ValueType& unused, UnpackTypes&... in)
{
- if (!handled.test(Index) && unpackValue<ValueType>::isRequired::value)
+ if (!handled.test(Index) && !is_optional_v<ValueType>)
{
messages::propertyMissing(res, key);
}