Update json helper function - readJson

The json helper functions are currently coupled
with response(crow::response) and limiting it to
use only while sending response. There are some
use cases where we don't send crow::response
instead we extract json data from files and load
configuration.

Decoupled the business logic for validating json
key existence, json value type checking and range
validation with top level use cases. So this json
helper functions can be used for validating json
and extracting value from json object during
init cases also(Ex: Used in EventService config
initialization)

Added new API in helper function which checks
key existence, value is of desired Type and
value is in acceptable range and assign value
in output parameter.

Tested:
Verified post and patch methods with different
kinds of value types( int, bool, string, vector
array, json etc..) and it works.

Change-Id: I21a6f9a12aef09f6ca79a02bc01d96efeeb3a20a
Signed-off-by: AppaRao Puli <apparao.puli@linux.intel.com>
diff --git a/redfish-core/include/utils/json_utils.hpp b/redfish-core/include/utils/json_utils.hpp
index 86bd97c..de732ce 100644
--- a/redfish-core/include/utils/json_utils.hpp
+++ b/redfish-core/include/utils/json_utils.hpp
@@ -78,22 +78,27 @@
 template <typename Type>
 constexpr bool is_std_array_v = is_std_array<Type>::value;
 
+enum class UnpackErrorCode
+{
+    success,
+    invalidType,
+    outOfRange
+};
+
 template <typename ToType, typename FromType>
-bool checkRange(const FromType& from, const std::string& key,
-                nlohmann::json& jsonValue, crow::Response& res)
+bool checkRange(const FromType& from, nlohmann::json& jsonValue,
+                const std::string& key)
 {
     if (from > std::numeric_limits<ToType>::max())
     {
         BMCWEB_LOG_DEBUG << "Value for key " << key
                          << " was greater than max: " << __PRETTY_FUNCTION__;
-        messages::propertyValueNotInList(res, jsonValue.dump(), key);
         return false;
     }
     if (from < std::numeric_limits<ToType>::lowest())
     {
         BMCWEB_LOG_DEBUG << "Value for key " << key
                          << " was less than min: " << __PRETTY_FUNCTION__;
-        messages::propertyValueNotInList(res, jsonValue.dump(), key);
         return false;
     }
     if constexpr (std::is_floating_point_v<ToType>)
@@ -101,7 +106,6 @@
         if (std::isnan(from))
         {
             BMCWEB_LOG_DEBUG << "Value for key " << key << " was NAN";
-            messages::propertyValueNotInList(res, jsonValue.dump(), key);
             return false;
         }
     }
@@ -110,10 +114,10 @@
 }
 
 template <typename Type>
-bool unpackValue(nlohmann::json& jsonValue, const std::string& key,
-                 crow::Response& res, Type& value)
+UnpackErrorCode unpackValueWithErrorCode(nlohmann::json& jsonValue,
+                                         const std::string& key, Type& value)
 {
-    bool ret = true;
+    UnpackErrorCode ret = UnpackErrorCode::success;
 
     if constexpr (std::is_floating_point_v<Type>)
     {
@@ -131,12 +135,11 @@
         }
         if (jsonPtr == nullptr)
         {
-            messages::propertyValueTypeError(res, jsonValue.dump(), key);
-            return false;
+            return UnpackErrorCode::invalidType;
         }
-        if (!checkRange<Type>(*jsonPtr, key, jsonValue, res))
+        if (!checkRange<Type>(*jsonPtr, jsonValue, key))
         {
-            return false;
+            return UnpackErrorCode::outOfRange;
         }
         value = static_cast<Type>(*jsonPtr);
     }
@@ -146,12 +149,11 @@
         int64_t* jsonPtr = jsonValue.get_ptr<int64_t*>();
         if (jsonPtr == nullptr)
         {
-            messages::propertyValueTypeError(res, jsonValue.dump(), key);
-            return false;
+            return UnpackErrorCode::invalidType;
         }
-        if (!checkRange<Type>(*jsonPtr, key, jsonValue, res))
+        if (!checkRange<Type>(*jsonPtr, jsonValue, key))
         {
-            return false;
+            return UnpackErrorCode::outOfRange;
         }
         value = static_cast<Type>(*jsonPtr);
     }
@@ -162,23 +164,15 @@
         uint64_t* jsonPtr = jsonValue.get_ptr<uint64_t*>();
         if (jsonPtr == nullptr)
         {
-            messages::propertyValueTypeError(res, jsonValue.dump(), key);
-            return false;
+            return UnpackErrorCode::invalidType;
         }
-        if (!checkRange<Type>(*jsonPtr, key, jsonValue, res))
+        if (!checkRange<Type>(*jsonPtr, jsonValue, key))
         {
-            return false;
+            return UnpackErrorCode::outOfRange;
         }
         value = static_cast<Type>(*jsonPtr);
     }
 
-    else if constexpr (is_optional_v<Type>)
-    {
-        value.emplace();
-        ret = unpackValue<typename Type::value_type>(jsonValue, key, res,
-                                                     *value) &&
-              ret;
-    }
     else if constexpr (std::is_same_v<nlohmann::json, Type>)
     {
         // Must be a complex type.  Simple types (int string etc) should be
@@ -186,12 +180,40 @@
         if (!jsonValue.is_object() && !jsonValue.is_array() &&
             !jsonValue.is_null())
         {
-            messages::propertyValueTypeError(res, jsonValue.dump(), key);
-            return false;
+            return UnpackErrorCode::invalidType;
         }
 
         value = std::move(jsonValue);
     }
+    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();
+            return UnpackErrorCode::invalidType;
+        }
+        value = std::move(*jsonPtr);
+    }
+    return ret;
+}
+
+template <typename Type>
+bool unpackValue(nlohmann::json& jsonValue, const std::string& key,
+                 crow::Response& res, Type& value)
+{
+    bool ret = true;
+
+    if constexpr (is_optional_v<Type>)
+    {
+        value.emplace();
+        ret = unpackValue<typename Type::value_type>(jsonValue, key, res,
+                                                     *value) &&
+              ret;
+    }
     else if constexpr (is_std_array_v<Type>)
     {
         if (!jsonValue.is_array())
@@ -230,18 +252,76 @@
     }
     else
     {
-        using JsonType = std::add_const_t<std::add_pointer_t<Type>>;
-        JsonType jsonPtr = jsonValue.get_ptr<JsonType>();
-        if (jsonPtr == nullptr)
+        UnpackErrorCode ec = unpackValueWithErrorCode(jsonValue, key, value);
+        if (ec != UnpackErrorCode::success)
         {
-            BMCWEB_LOG_DEBUG
-                << "Value for key " << key
-                << " was incorrect type: " << jsonValue.type_name();
-            messages::propertyValueTypeError(res, jsonValue.dump(), key);
+            if (ec == UnpackErrorCode::invalidType)
+            {
+                messages::propertyValueTypeError(res, jsonValue.dump(), key);
+            }
+            else if (ec == UnpackErrorCode::outOfRange)
+            {
+                messages::propertyValueNotInList(res, jsonValue.dump(), key);
+            }
             return false;
         }
-        value = std::move(*jsonPtr);
     }
+
+    return ret;
+}
+
+template <typename Type>
+bool unpackValue(nlohmann::json& jsonValue, const std::string& key, Type& value)
+{
+    bool ret = true;
+    if constexpr (is_optional_v<Type>)
+    {
+        value.emplace();
+        ret = unpackValue<typename Type::value_type>(jsonValue, key, *value) &&
+              ret;
+    }
+    else if constexpr (is_std_array_v<Type>)
+    {
+        if (!jsonValue.is_array())
+        {
+            return false;
+        }
+        if (jsonValue.size() != value.size())
+        {
+            return false;
+        }
+        size_t index = 0;
+        for (const auto& val : jsonValue.items())
+        {
+            ret = unpackValue<typename Type::value_type>(val.value(), key,
+                                                         value[index++]) &&
+                  ret;
+        }
+    }
+    else if constexpr (is_vector_v<Type>)
+    {
+        if (!jsonValue.is_array())
+        {
+            return false;
+        }
+
+        for (const auto& val : jsonValue.items())
+        {
+            value.emplace_back();
+            ret = unpackValue<typename Type::value_type>(val.value(), key,
+                                                         value.back()) &&
+                  ret;
+        }
+    }
+    else
+    {
+        UnpackErrorCode ec = unpackValueWithErrorCode(jsonValue, key, value);
+        if (ec != UnpackErrorCode::success)
+        {
+            return false;
+        }
+    }
+
     return ret;
 }
 
@@ -342,5 +422,19 @@
     return readJson(jsonRequest, res, key, in...);
 }
 
+template <typename Type>
+bool getValueFromJsonObject(nlohmann::json& jsonData, const std::string& key,
+                            Type& value)
+{
+    nlohmann::json jsonValue = jsonData[key];
+    if (jsonValue.is_null())
+    {
+        BMCWEB_LOG_DEBUG << "Key " << key << " not exist";
+        return false;
+    }
+
+    return details::unpackValue(jsonValue, key, value);
+}
+
 } // namespace json_util
 } // namespace redfish