xref: /openbmc/bmcweb/features/redfish/include/utils/json_utils.hpp (revision 5eb2bef287eff01515671f07148393ceb9c5cad6)
177dd8813SKowalski, Kamil /*
277dd8813SKowalski, Kamil // Copyright (c) 2018 Intel Corporation
377dd8813SKowalski, Kamil //
477dd8813SKowalski, Kamil // Licensed under the Apache License, Version 2.0 (the "License");
577dd8813SKowalski, Kamil // you may not use this file except in compliance with the License.
677dd8813SKowalski, Kamil // You may obtain a copy of the License at
777dd8813SKowalski, Kamil //
877dd8813SKowalski, Kamil //      http://www.apache.org/licenses/LICENSE-2.0
977dd8813SKowalski, Kamil //
1077dd8813SKowalski, Kamil // Unless required by applicable law or agreed to in writing, software
1177dd8813SKowalski, Kamil // distributed under the License is distributed on an "AS IS" BASIS,
1277dd8813SKowalski, Kamil // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1377dd8813SKowalski, Kamil // See the License for the specific language governing permissions and
1477dd8813SKowalski, Kamil // limitations under the License.
1577dd8813SKowalski, Kamil */
1677dd8813SKowalski, Kamil #pragma once
179712f8acSEd Tanous 
18c94ad49bSEd Tanous #include <http_request.h>
19c94ad49bSEd Tanous #include <http_response.h>
2077dd8813SKowalski, Kamil 
219712f8acSEd Tanous #include <bitset>
229712f8acSEd Tanous #include <error_messages.hpp>
231abe55efSEd Tanous #include <nlohmann/json.hpp>
240627a2c7SEd Tanous 
251abe55efSEd Tanous namespace redfish
261abe55efSEd Tanous {
271abe55efSEd Tanous 
281abe55efSEd Tanous namespace json_util
291abe55efSEd Tanous {
3077dd8813SKowalski, Kamil 
3177dd8813SKowalski, Kamil /**
3277dd8813SKowalski, Kamil  * @brief Processes request to extract JSON from its body. If it fails, adds
3377dd8813SKowalski, Kamil  *       MalformedJSON message to response and ends it.
3477dd8813SKowalski, Kamil  *
3577dd8813SKowalski, Kamil  * @param[io]  res       Response object
3677dd8813SKowalski, Kamil  * @param[in]  req       Request object
3777dd8813SKowalski, Kamil  * @param[out] reqJson   JSON object extracted from request's body
3877dd8813SKowalski, Kamil  *
3977dd8813SKowalski, Kamil  * @return true if JSON is valid, false when JSON is invalid and response has
4077dd8813SKowalski, Kamil  *         been filled with message and ended.
4177dd8813SKowalski, Kamil  */
4255c7b7a2SEd Tanous bool processJsonFromRequest(crow::Response& res, const crow::Request& req,
4377dd8813SKowalski, Kamil                             nlohmann::json& reqJson);
449712f8acSEd Tanous namespace details
459712f8acSEd Tanous {
46771cfa0fSJason M. Bills 
47771cfa0fSJason M. Bills template <typename Type> struct is_optional : std::false_type
489712f8acSEd Tanous {
499712f8acSEd Tanous };
509712f8acSEd Tanous 
51771cfa0fSJason M. Bills template <typename Type>
52a24526dcSEd Tanous struct is_optional<std::optional<Type>> : std::true_type
539712f8acSEd Tanous {
549712f8acSEd Tanous };
559712f8acSEd Tanous 
56771cfa0fSJason M. Bills template <typename Type>
57771cfa0fSJason M. Bills constexpr bool is_optional_v = is_optional<Type>::value;
58771cfa0fSJason M. Bills 
59b1556427SEd Tanous template <typename Type> struct is_vector : std::false_type
60b1556427SEd Tanous {
61b1556427SEd Tanous };
62b1556427SEd Tanous 
63b1556427SEd Tanous template <typename Type> struct is_vector<std::vector<Type>> : std::true_type
64b1556427SEd Tanous {
65b1556427SEd Tanous };
66b1556427SEd Tanous 
67b1556427SEd Tanous template <typename Type> constexpr bool is_vector_v = is_vector<Type>::value;
68b1556427SEd Tanous 
69318226c2SJames Feist template <typename Type> struct is_std_array : std::false_type
70318226c2SJames Feist {
71318226c2SJames Feist };
72318226c2SJames Feist 
73318226c2SJames Feist template <typename Type, std::size_t size>
74318226c2SJames Feist struct is_std_array<std::array<Type, size>> : std::true_type
75318226c2SJames Feist {
76318226c2SJames Feist };
77318226c2SJames Feist 
78318226c2SJames Feist template <typename Type>
79318226c2SJames Feist constexpr bool is_std_array_v = is_std_array<Type>::value;
80318226c2SJames Feist 
81a6acbb31SJames Feist template <typename ToType, typename FromType>
82ee344e0fSEd Tanous bool checkRange(const FromType& from, const std::string& key,
83a6acbb31SJames Feist                 nlohmann::json& jsonValue, crow::Response& res)
84a6acbb31SJames Feist {
85ee344e0fSEd Tanous     if (from > std::numeric_limits<ToType>::max())
86a6acbb31SJames Feist     {
87a6acbb31SJames Feist         BMCWEB_LOG_DEBUG << "Value for key " << key
88a6acbb31SJames Feist                          << " was greater than max: " << __PRETTY_FUNCTION__;
89a6acbb31SJames Feist         messages::propertyValueNotInList(res, jsonValue.dump(), key);
90a6acbb31SJames Feist         return false;
91a6acbb31SJames Feist     }
92ee344e0fSEd Tanous     if (from < std::numeric_limits<ToType>::lowest())
93a6acbb31SJames Feist     {
94a6acbb31SJames Feist         BMCWEB_LOG_DEBUG << "Value for key " << key
95a6acbb31SJames Feist                          << " was less than min: " << __PRETTY_FUNCTION__;
96a6acbb31SJames Feist         messages::propertyValueNotInList(res, jsonValue.dump(), key);
97a6acbb31SJames Feist         return false;
98a6acbb31SJames Feist     }
99a6acbb31SJames Feist     if constexpr (std::is_floating_point_v<ToType>)
100a6acbb31SJames Feist     {
101ee344e0fSEd Tanous         if (std::isnan(from))
102a6acbb31SJames Feist         {
103a6acbb31SJames Feist             BMCWEB_LOG_DEBUG << "Value for key " << key << " was NAN";
104a6acbb31SJames Feist             messages::propertyValueNotInList(res, jsonValue.dump(), key);
105a6acbb31SJames Feist             return false;
106a6acbb31SJames Feist         }
107a6acbb31SJames Feist     }
108a6acbb31SJames Feist 
109a6acbb31SJames Feist     return true;
110a6acbb31SJames Feist }
111a6acbb31SJames Feist 
112771cfa0fSJason M. Bills template <typename Type>
11341352c24SSantosh Puranik bool unpackValue(nlohmann::json& jsonValue, const std::string& key,
114771cfa0fSJason M. Bills                  crow::Response& res, Type& value)
115771cfa0fSJason M. Bills {
11641352c24SSantosh Puranik     bool ret = true;
11741352c24SSantosh Puranik 
118a6acbb31SJames Feist     if constexpr (std::is_floating_point_v<Type>)
119771cfa0fSJason M. Bills     {
120a6acbb31SJames Feist         double helper = 0;
121a6acbb31SJames Feist         double* jsonPtr = jsonValue.get_ptr<double*>();
122771cfa0fSJason M. Bills 
123771cfa0fSJason M. Bills         if (jsonPtr == nullptr)
124771cfa0fSJason M. Bills         {
125a6acbb31SJames Feist             int64_t* intPtr = jsonValue.get_ptr<int64_t*>();
126a6acbb31SJames Feist             if (intPtr != nullptr)
127771cfa0fSJason M. Bills             {
128a6acbb31SJames Feist                 helper = static_cast<double>(*intPtr);
129a6acbb31SJames Feist                 jsonPtr = &helper;
130771cfa0fSJason M. Bills             }
131a6acbb31SJames Feist         }
132*5eb2bef2SAppaRao Puli         if (jsonPtr == nullptr)
133*5eb2bef2SAppaRao Puli         {
134*5eb2bef2SAppaRao Puli             messages::propertyValueTypeError(res, jsonValue.dump(), key);
135*5eb2bef2SAppaRao Puli             return false;
136*5eb2bef2SAppaRao Puli         }
137ee344e0fSEd Tanous         if (!checkRange<Type>(*jsonPtr, key, jsonValue, res))
138771cfa0fSJason M. Bills         {
13941352c24SSantosh Puranik             return false;
140771cfa0fSJason M. Bills         }
141771cfa0fSJason M. Bills         value = static_cast<Type>(*jsonPtr);
142771cfa0fSJason M. Bills     }
143a6acbb31SJames Feist 
144a6acbb31SJames Feist     else if constexpr (std::is_signed_v<Type>)
145a6acbb31SJames Feist     {
146a6acbb31SJames Feist         int64_t* jsonPtr = jsonValue.get_ptr<int64_t*>();
147271584abSEd Tanous         if (jsonPtr == nullptr)
148271584abSEd Tanous         {
149*5eb2bef2SAppaRao Puli             messages::propertyValueTypeError(res, jsonValue.dump(), key);
150271584abSEd Tanous             return false;
151271584abSEd Tanous         }
152ee344e0fSEd Tanous         if (!checkRange<Type>(*jsonPtr, key, jsonValue, res))
153a6acbb31SJames Feist         {
15441352c24SSantosh Puranik             return false;
155a6acbb31SJames Feist         }
156a6acbb31SJames Feist         value = static_cast<Type>(*jsonPtr);
157a6acbb31SJames Feist     }
158a6acbb31SJames Feist 
1598102ddbaSAppaRao Puli     else if constexpr ((std::is_unsigned_v<Type>)&&(
1608102ddbaSAppaRao Puli                            !std::is_same_v<bool, Type>))
161a6acbb31SJames Feist     {
162a6acbb31SJames Feist         uint64_t* jsonPtr = jsonValue.get_ptr<uint64_t*>();
163271584abSEd Tanous         if (jsonPtr == nullptr)
164271584abSEd Tanous         {
165*5eb2bef2SAppaRao Puli             messages::propertyValueTypeError(res, jsonValue.dump(), key);
166271584abSEd Tanous             return false;
167271584abSEd Tanous         }
168ee344e0fSEd Tanous         if (!checkRange<Type>(*jsonPtr, key, jsonValue, res))
169a6acbb31SJames Feist         {
17041352c24SSantosh Puranik             return false;
171a6acbb31SJames Feist         }
172a6acbb31SJames Feist         value = static_cast<Type>(*jsonPtr);
173a6acbb31SJames Feist     }
174a6acbb31SJames Feist 
175771cfa0fSJason M. Bills     else if constexpr (is_optional_v<Type>)
176771cfa0fSJason M. Bills     {
177771cfa0fSJason M. Bills         value.emplace();
17841352c24SSantosh Puranik         ret = unpackValue<typename Type::value_type>(jsonValue, key, res,
17941352c24SSantosh Puranik                                                      *value) &&
18041352c24SSantosh Puranik               ret;
181771cfa0fSJason M. Bills     }
1820627a2c7SEd Tanous     else if constexpr (std::is_same_v<nlohmann::json, Type>)
1830627a2c7SEd Tanous     {
1840627a2c7SEd Tanous         // Must be a complex type.  Simple types (int string etc) should be
1850627a2c7SEd Tanous         // unpacked directly
1868ebc91f6SEd Tanous         if (!jsonValue.is_object() && !jsonValue.is_array() &&
1878ebc91f6SEd Tanous             !jsonValue.is_null())
1880627a2c7SEd Tanous         {
1890627a2c7SEd Tanous             messages::propertyValueTypeError(res, jsonValue.dump(), key);
19041352c24SSantosh Puranik             return false;
1910627a2c7SEd Tanous         }
1920627a2c7SEd Tanous 
1930627a2c7SEd Tanous         value = std::move(jsonValue);
1940627a2c7SEd Tanous     }
195318226c2SJames Feist     else if constexpr (is_std_array_v<Type>)
196318226c2SJames Feist     {
197318226c2SJames Feist         if (!jsonValue.is_array())
198318226c2SJames Feist         {
199318226c2SJames Feist             messages::propertyValueTypeError(res, res.jsonValue.dump(), key);
20041352c24SSantosh Puranik             return false;
201318226c2SJames Feist         }
202318226c2SJames Feist         if (jsonValue.size() != value.size())
203318226c2SJames Feist         {
204318226c2SJames Feist             messages::propertyValueTypeError(res, res.jsonValue.dump(), key);
20541352c24SSantosh Puranik             return false;
206318226c2SJames Feist         }
207318226c2SJames Feist         size_t index = 0;
208318226c2SJames Feist         for (const auto& val : jsonValue.items())
209318226c2SJames Feist         {
21041352c24SSantosh Puranik             ret = unpackValue<typename Type::value_type>(val.value(), key, res,
21141352c24SSantosh Puranik                                                          value[index++]) &&
21241352c24SSantosh Puranik                   ret;
213318226c2SJames Feist         }
214318226c2SJames Feist     }
215b1556427SEd Tanous     else if constexpr (is_vector_v<Type>)
216b1556427SEd Tanous     {
217b1556427SEd Tanous         if (!jsonValue.is_array())
218b1556427SEd Tanous         {
219b1556427SEd Tanous             messages::propertyValueTypeError(res, res.jsonValue.dump(), key);
22041352c24SSantosh Puranik             return false;
221b1556427SEd Tanous         }
222b1556427SEd Tanous 
223b1556427SEd Tanous         for (const auto& val : jsonValue.items())
224b1556427SEd Tanous         {
225b1556427SEd Tanous             value.emplace_back();
22641352c24SSantosh Puranik             ret = unpackValue<typename Type::value_type>(val.value(), key, res,
22741352c24SSantosh Puranik                                                          value.back()) &&
22841352c24SSantosh Puranik                   ret;
229b1556427SEd Tanous         }
230b1556427SEd Tanous     }
231771cfa0fSJason M. Bills     else
232771cfa0fSJason M. Bills     {
233771cfa0fSJason M. Bills         using JsonType = std::add_const_t<std::add_pointer_t<Type>>;
234771cfa0fSJason M. Bills         JsonType jsonPtr = jsonValue.get_ptr<JsonType>();
235771cfa0fSJason M. Bills         if (jsonPtr == nullptr)
236771cfa0fSJason M. Bills         {
237771cfa0fSJason M. Bills             BMCWEB_LOG_DEBUG
238771cfa0fSJason M. Bills                 << "Value for key " << key
239771cfa0fSJason M. Bills                 << " was incorrect type: " << jsonValue.type_name();
240771cfa0fSJason M. Bills             messages::propertyValueTypeError(res, jsonValue.dump(), key);
24141352c24SSantosh Puranik             return false;
242771cfa0fSJason M. Bills         }
243771cfa0fSJason M. Bills         value = std::move(*jsonPtr);
244771cfa0fSJason M. Bills     }
24541352c24SSantosh Puranik     return ret;
246771cfa0fSJason M. Bills }
247771cfa0fSJason M. Bills 
2489712f8acSEd Tanous template <size_t Count, size_t Index>
24941352c24SSantosh Puranik bool readJsonValues(const std::string& key, nlohmann::json& jsonValue,
2509712f8acSEd Tanous                     crow::Response& res, std::bitset<Count>& handled)
2519712f8acSEd Tanous {
2529712f8acSEd Tanous     BMCWEB_LOG_DEBUG << "Unable to find variable for key" << key;
253a08b46ccSJason M. Bills     messages::propertyUnknown(res, key);
25441352c24SSantosh Puranik     return false;
2559712f8acSEd Tanous }
2569712f8acSEd Tanous 
2579712f8acSEd Tanous template <size_t Count, size_t Index, typename ValueType,
2589712f8acSEd Tanous           typename... UnpackTypes>
25941352c24SSantosh Puranik bool readJsonValues(const std::string& key, nlohmann::json& jsonValue,
2609712f8acSEd Tanous                     crow::Response& res, std::bitset<Count>& handled,
2619712f8acSEd Tanous                     const char* keyToCheck, ValueType& valueToFill,
2629712f8acSEd Tanous                     UnpackTypes&... in)
2639712f8acSEd Tanous {
26441352c24SSantosh Puranik     bool ret = true;
2659712f8acSEd Tanous     if (key != keyToCheck)
2669712f8acSEd Tanous     {
26741352c24SSantosh Puranik         ret = readJsonValues<Count, Index + 1>(key, jsonValue, res, handled,
26841352c24SSantosh Puranik                                                in...) &&
26941352c24SSantosh Puranik               ret;
27041352c24SSantosh Puranik         return ret;
2719712f8acSEd Tanous     }
2729712f8acSEd Tanous 
2739712f8acSEd Tanous     handled.set(Index);
2749712f8acSEd Tanous 
27541352c24SSantosh Puranik     return unpackValue<ValueType>(jsonValue, key, res, valueToFill) && ret;
2769712f8acSEd Tanous }
2779712f8acSEd Tanous 
2789712f8acSEd Tanous template <size_t Index = 0, size_t Count>
27941352c24SSantosh Puranik bool handleMissing(std::bitset<Count>& handled, crow::Response& res)
2809712f8acSEd Tanous {
28141352c24SSantosh Puranik     return true;
2829712f8acSEd Tanous }
2839712f8acSEd Tanous 
2849712f8acSEd Tanous template <size_t Index = 0, size_t Count, typename ValueType,
2859712f8acSEd Tanous           typename... UnpackTypes>
28641352c24SSantosh Puranik bool handleMissing(std::bitset<Count>& handled, crow::Response& res,
2879712f8acSEd Tanous                    const char* key, ValueType& unused, UnpackTypes&... in)
2889712f8acSEd Tanous {
28941352c24SSantosh Puranik     bool ret = true;
290771cfa0fSJason M. Bills     if (!handled.test(Index) && !is_optional_v<ValueType>)
2919712f8acSEd Tanous     {
29241352c24SSantosh Puranik         ret = false;
293a08b46ccSJason M. Bills         messages::propertyMissing(res, key);
2949712f8acSEd Tanous     }
29541352c24SSantosh Puranik     return details::handleMissing<Index + 1, Count>(handled, res, in...) && ret;
2969712f8acSEd Tanous }
2979712f8acSEd Tanous } // namespace details
2989712f8acSEd Tanous 
2999712f8acSEd Tanous template <typename... UnpackTypes>
3000627a2c7SEd Tanous bool readJson(nlohmann::json& jsonRequest, crow::Response& res, const char* key,
3019712f8acSEd Tanous               UnpackTypes&... in)
3029712f8acSEd Tanous {
30341352c24SSantosh Puranik     bool result = true;
3049712f8acSEd Tanous     if (!jsonRequest.is_object())
3059712f8acSEd Tanous     {
3069712f8acSEd Tanous         BMCWEB_LOG_DEBUG << "Json value is not an object";
307f12894f8SJason M. Bills         messages::unrecognizedRequestBody(res);
3089712f8acSEd Tanous         return false;
3099712f8acSEd Tanous     }
3109712f8acSEd Tanous 
3119712f8acSEd Tanous     if (jsonRequest.empty())
3129712f8acSEd Tanous     {
3139712f8acSEd Tanous         BMCWEB_LOG_DEBUG << "Json value is empty";
314f12894f8SJason M. Bills         messages::emptyJSON(res);
3159712f8acSEd Tanous         return false;
3169712f8acSEd Tanous     }
3179712f8acSEd Tanous 
3189712f8acSEd Tanous     std::bitset<(sizeof...(in) + 1) / 2> handled(0);
3199712f8acSEd Tanous     for (const auto& item : jsonRequest.items())
3209712f8acSEd Tanous     {
32141352c24SSantosh Puranik         result =
3229712f8acSEd Tanous             details::readJsonValues<(sizeof...(in) + 1) / 2, 0, UnpackTypes...>(
32341352c24SSantosh Puranik                 item.key(), item.value(), res, handled, key, in...) &&
32441352c24SSantosh Puranik             result;
3259712f8acSEd Tanous     }
3269712f8acSEd Tanous 
32741352c24SSantosh Puranik     BMCWEB_LOG_DEBUG << "JSON result is: " << result;
3289712f8acSEd Tanous 
32941352c24SSantosh Puranik     return details::handleMissing(handled, res, key, in...) && result;
3309712f8acSEd Tanous }
33177dd8813SKowalski, Kamil 
3320627a2c7SEd Tanous template <typename... UnpackTypes>
3330627a2c7SEd Tanous bool readJson(const crow::Request& req, crow::Response& res, const char* key,
3340627a2c7SEd Tanous               UnpackTypes&... in)
3350627a2c7SEd Tanous {
3360627a2c7SEd Tanous     nlohmann::json jsonRequest;
3370627a2c7SEd Tanous     if (!json_util::processJsonFromRequest(res, req, jsonRequest))
3380627a2c7SEd Tanous     {
3390627a2c7SEd Tanous         BMCWEB_LOG_DEBUG << "Json value not readable";
3400627a2c7SEd Tanous         return false;
3410627a2c7SEd Tanous     }
3420627a2c7SEd Tanous     return readJson(jsonRequest, res, key, in...);
3430627a2c7SEd Tanous }
3440627a2c7SEd Tanous 
34577dd8813SKowalski, Kamil } // namespace json_util
34677dd8813SKowalski, Kamil } // namespace redfish
347